Update googletest to 0.13.0
Bug: 388473101
Test: Code compiles
Ignore-AOSP-First: Requires changes in internal-only projects
Change-Id: Ia6cc0fad1ca07e3b1d34f4c13e629055aa7e1056
diff --git a/crates/googletest/.android-checksum.json b/crates/googletest/.android-checksum.json
index 2db3741..774a9d6 100644
--- a/crates/googletest/.android-checksum.json
+++ b/crates/googletest/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{".cargo-checksum.json":"d6b2b0d30a03b5a480e0c9a54b655d8eb406a699a4d54a1b79f086264f813155","Android.bp":"26dbfa23b3d723fa6e4cff6247a4574d3de9e15abf7ea529443ea6279f723c08","Cargo.toml":"a5ea2564b9871a41486fa1e377e576e5f1f3f173de5812f14ac7cd6f0eff3dce","LICENSE":"7c6512d88b3127990067585f24881ba1f182c5c49a04cb1975b226b7be95709e","METADATA":"699e61a9efcc1e3a7534f11530d9486333cc82b7bbe820d66aba3d6fc4d41a6e","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"07d4f57eac87d9661107bd8ae50f13f05e63ab60a22a5d6e84159a446412f658","cargo_embargo.json":"2b7db5752ae086bd42bc615742f3fe1183fff050dd0fe67aea0c4519d4b592d3","crate_docs.md":"22353450101e1ea34aebd2263b5c9f6b0fc2dca5d876d2a755b52fa9c0a570b0","src/assertions.rs":"dd45066e3462691b73cf78ea08d86bc6106c9d8b3292ea67de80f7c65d5425ab","src/description.rs":"f342abc02a9808cd6ac8efbb0dc5653cf75960fe0e6b65c604a02eb33d18f078","src/internal/description_renderer.rs":"ad2cfa43baa63ec7c33b73cfb66f16fe7fad6379576bd896ab22a1ec418255f0","src/internal/mod.rs":"13cf778b7e517506ee21cce6c347c409ff476a805b76874a320e350a7f5f86de","src/internal/source_location.rs":"2a0bfa8a6556301f4b469711568741d601f3cc35bd46efa48887d123e1ad6cf4","src/internal/test_outcome.rs":"bd48ed72e39f7cb48ab8591ee0786d4aa810a49877e4a300a949dde27fc28f00","src/lib.rs":"e79ac41262f17c185c57c6993ac2fd3aad1f2c127f160451fdfe40f9c2dd6570","src/matcher.rs":"6c4f8bb7c1869c5a4e4bfe4a6fa88ea253256fceacd7d11a71299f17b044a4fb","src/matcher_support/count_elements.rs":"7487aa09ddfd68e7786943bc68ba97fb54857441e9fb7160f5eac1785a1d2176","src/matcher_support/edit_distance.rs":"0cf896f82ccaed727c0a1c7e2578fd1430d812fe52168e17b08e53b185385c81","src/matcher_support/mod.rs":"9e594fb5d2ae79a34c451f21beb1912cf9c05646156a8bcb482dccf55adece03","src/matcher_support/summarize_diff.rs":"7fedfdc6228307cce6da7ff3519be8d2c516c9e2f5fbdbc05ce679e476877024","src/matcher_support/zipped_iterator.rs":"2c022b163bb1defda1522df9ff6824c0db0044e9314cdd45dfe39e2e1e335b96","src/matchers/all_matcher.rs":"f58ad8690c1237eb53c7d745ef9df667f6c38dc19e02d1a0641d563392feb6f3","src/matchers/any_matcher.rs":"d5c4d03d9f515bed370ca538013b6d6d8d62c57309fef9decd3c4b684a73e4a9","src/matchers/anything_matcher.rs":"664c706c76d7603664a81f043589f5ab6cf1e9585bc307ab07a16df4d75cad32","src/matchers/char_count_matcher.rs":"2707f530252e29c45b953e4d5fb9c86e0ff62041b07ce6331cb9cf7db9858e2a","src/matchers/conjunction_matcher.rs":"8cb736a15d2be8405e200d6599e2c6d8e86b79c27822a3bde2361fdd3c9d6a2d","src/matchers/container_eq_matcher.rs":"97eded933a75fc0a42905c455c53a31bea88b5f65cb7a072214313db72c97d84","src/matchers/contains_matcher.rs":"297cdbcf152caca7593f809fb805bedc25c5448157f351c63cbaa97e10304c20","src/matchers/contains_regex_matcher.rs":"d818d0aac0302fb9aaa805aa72cbec240c1303b59e59b85a070a983796a068aa","src/matchers/disjunction_matcher.rs":"a0e72b4c6aaf33c468f74ef9d276006e800ad675a8e572c9310271a4cb2b0d0e","src/matchers/display_matcher.rs":"2a73d326a2e547abbebc58c401128649187ec57aafaf3a71cde58ec0611646b7","src/matchers/each_matcher.rs":"b0e0d70598601135f31715823cbeb2c1709900b11d07c1d9305e37f31adff61a","src/matchers/elements_are_matcher.rs":"16e10c4bf6755c26ec72f6343aa1b363c8260dd387561c9ab2595e3dec84b46e","src/matchers/empty_matcher.rs":"6d29920215cb48e880a1fa0519727467e83ab74cccd3472b9aa2bae016b7d35c","src/matchers/eq_deref_of_matcher.rs":"9d1fe8c2094a3649ace379731af601b6c4abb1aed412a718d25dcd191f1c9496","src/matchers/eq_matcher.rs":"e9b985a9cc3bbd471aead0cd7100b0725edd4faea0d35713f43385ee19ca3af7","src/matchers/err_matcher.rs":"0da1c872fded683fd515786c5b8aab8ee99c3bd8d8ba35ae74ac354473393447","src/matchers/field_matcher.rs":"50b4589bac6222e3e7a3aa1c140fda70a259fde39b538b7513e3305362c675e3","src/matchers/ge_matcher.rs":"af4641c64a43fa29c9d3878443a62dca64620c311e1957609277ea707fcf5a7a","src/matchers/gt_matcher.rs":"d9bf9d216f61335e067a39f421ef1e3baff2871776f6287541f06711c23a2733","src/matchers/has_entry_matcher.rs":"f2ec364e57853468c941f52329dab2251b581a7c2deca69e953b4b94be509e63","src/matchers/is_encoded_string_matcher.rs":"f95f3fed1b0e161829983fc50077dd931f5cc16e7fedfec2de088a7083d926c4","src/matchers/is_matcher.rs":"6a2a68665a079e21406da1454408d606e62c5e21ee07ae94e7eeb4335ea3f044","src/matchers/is_nan_matcher.rs":"33a179908c1f0ab95351c6ec623d5d0dc494bfb64cd8df2ff4244f01916395e7","src/matchers/le_matcher.rs":"c2c6818514773564ac578ffe86e5e2b9bff58785ddab0b37697c4bb80bea38dd","src/matchers/len_matcher.rs":"015b86c053595e3fbc7ede581ab03b5e168369b6f8cd133f3b479c181f474001","src/matchers/lt_matcher.rs":"c937c756416f7ba5f5ea907f9ae5539ad9520c33227346c4abd481c8e70330f1","src/matchers/matches_pattern.rs":"f17968108e733459a06fbb0709ed7b76ce8245e183925c06d8575545f5c23071","src/matchers/matches_regex_matcher.rs":"3d95e01bdb64b1081cfccb11465edb94dfde8566ea3950d03fefb75eab854189","src/matchers/mod.rs":"09235b20fb2166d2d5c72316a9b3ecb736344b7a9636855bd9d94bb1fb5b4831","src/matchers/near_matcher.rs":"645e70df5f2f4c1d3e3b89d377296e21a30cb90d34426069f5aae36fe29206cb","src/matchers/none_matcher.rs":"d1a13d3de32e7ba5781accf9d01104cc54d0a9e6df3a38cd00768ea2f6eeb18e","src/matchers/not_matcher.rs":"43ec462558eec8021ef238a72541433210fe9915be235a5845cbc9217e8f0f23","src/matchers/ok_matcher.rs":"f358885ba1be7f8b24ad658788c9c1673f03950fda6b166d2eecd8eb1899efce","src/matchers/points_to_matcher.rs":"30f88451c8806aee81d0f9925583f15531773399089e54ad2fe4b81544b45fb8","src/matchers/pointwise_matcher.rs":"8dd05061cb3a86cde777c16f4b9d84dab6469084e8878dabfe4f34748555a96d","src/matchers/predicate_matcher.rs":"e18e614fdb2a4ce28ae92b33fd605641b9c9720391e0e9d067f90b7060b62958","src/matchers/property_matcher.rs":"8507ec831853e9947f57fece53bf7e79b1c902290ae1cf55284186b8f06b354b","src/matchers/some_matcher.rs":"c643fe7aae9fac6565894b83080a352c3f843c785e44e27ad3053eccee141403","src/matchers/str_matcher.rs":"37657e56b32530124d04903ec7622a35881cbb39fed0b719a24c1362b2ab8574","src/matchers/subset_of_matcher.rs":"1bf4fee1617e65744b3ec9cc18721783804ed84fd27d7d7f253b5fc82979b6bc","src/matchers/superset_of_matcher.rs":"d1a8dbd21113d22eb1fef9fcf45d136a388a6ce4622f8244344b0b81d2856144","src/matchers/tuple_matcher.rs":"9e08b6b4e7bb5cce0f47047ca89a34d058d634eeeb13357c3c8140962f20a1cd","src/matchers/unordered_elements_are_matcher.rs":"802cad45924cdfcf1d1a72b83aa01a0c06d7e6b01a219f76638af3cacae69056","tests/all_matcher_test.rs":"91a95c6aca516f2cedf7ecb297280ae52b5cf96e25eafe29cd0b4bf40493b5be","tests/any_matcher_test.rs":"e8aae6ac19e3a8501cd0101cda2cc0c5fd49ce05bf81651cec925e2f01eaedfb","tests/colorized_diff_test.rs":"9632e5d8460f67331df2edda7848db3b79bcefa043cd44d8aab7eafc90945dae","tests/composition_test.rs":"dfe726b38d357ff8d547eeddbdca539b8bf70f6681125f063c88db6140c8bf65","tests/elements_are_matcher_test.rs":"5ccaafb93fec5d9790edefb2d06ed5376f898e8e971d9663614bffcc939a96e0","tests/field_matcher_test.rs":"b3e7f0f6d403d4dc768d282128ae6eda68a96fcf1cd3127c107cf68f423a0bf5","tests/lib.rs":"d48d840a933a9a9e1076865eb6e443b538d4f37d94b45b7110333511f9a50d9d","tests/matches_pattern_test.rs":"35101434bbb3dea2e3054dfc55381a886d2055002156ebb2fa1575fef6386a63","tests/no_color_test.rs":"13795788bd9045f25f040067cb359e4d55505ddf6a1524922db23f3aeff570af","tests/pointwise_matcher_test.rs":"e0c14a1e030f83c3ebfae00d73c49af60d5958836aa8264ce4a3374330196f5f","tests/property_matcher_test.rs":"57da661e975d2af939ffd3bbd5fac7d11b2d344fa62d709a78080a68053370f4","tests/proptest_integration_test.rs":"0f75081bd6bfd89b8a5e430238a9da78a2f13ce6ccc4e105e32705596e9c3a7f","tests/tuple_matcher_test.rs":"b16ce00f9857206f9ebaf18175c4d2df5de739fe85873fb0f30b1c69e794ffe3","tests/unordered_elements_are_matcher_test.rs":"2d3402327bf2915cc79a3fce01f939e4454d45dc8f53826211add79d389f3c29"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"410493270e72d4760c1e835f510b65d50a78811d6946a3f93170a161f8b9d815","Android.bp":"31cf3dbb287b6b5c5fd4dd46be662c4b64ca88577b495dc9436f8d5db5707bb0","Cargo.toml":"a194180b19517aeb9c05048610d5f7422c6e50113ebc55b5f381f06b22414b3d","LICENSE":"7c6512d88b3127990067585f24881ba1f182c5c49a04cb1975b226b7be95709e","METADATA":"8e573ebeca353ef9a3892bf4ba8ae4c0b0f781d737b2716ce2b791903a85540d","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"440fe5145aa23165197219e0378cbb71cd7ca336fb1dc0fe53a3d5a2e3d4471d","cargo_embargo.json":"2b7db5752ae086bd42bc615742f3fe1183fff050dd0fe67aea0c4519d4b592d3","crate_docs.md":"86974c308ad2e035d0368179b32191c32672353273cfe5ea970687a97849b016","src/assertions.rs":"3f9defd623a7006b23d3963caffdf701b78c8cf0696a94ead1541e43698d3866","src/description.rs":"7bf3fec806182d8a011b46ca0dd7f14adcd17ece7fe81dc7770a232ecb21b21f","src/fixtures.rs":"b8ebce97930de9822eed6fa6e1d0aadf9e8eda61c989ba27644878444babe143","src/fmt.rs":"f6e89ce1eac6215301c185762f0ca5214efeda1fb431a9adfa29dafec6bc238e","src/internal/description_renderer.rs":"22f0dafd9d02a37483bbd689409f01b001184d30c095459b21c61cf90df071dd","src/internal/mod.rs":"bfaca36e81794979c6f7976e96a6ac8f7b09d0181ac8f44538d40a2e40d8453d","src/internal/test_outcome.rs":"e8a39aa3bc29d79e64ad42da4f22a18452b76171d28d14be3cbb0b822f517299","src/lib.rs":"109c5e2b09705cb4bd11330e10c69daf2523b01422f103db6c10353823f6fccb","src/matcher.rs":"e80fadb4a9939d190e1fe94570e9f50ed2359842505d7862e34136cd8f6ac55d","src/matcher_support/auto_eq.rs":"5a6d9607a884b9a21e0ec29a8b75a78e741448ac858b618506521eb67a2d255f","src/matcher_support/count_elements.rs":"0973e4f0f73ea1d92b8cf6559aa696d84ae27179765f93fa03acfbd239a447ca","src/matcher_support/edit_distance.rs":"83aadc058939cf5338038650b9edb85fdf2ecf6d90ac35656eda69d7f4715102","src/matcher_support/mod.rs":"fc213e3f665b23bf34f639dae133920fd17db0a5c673d4957f9cbb3d4e706d71","src/matcher_support/summarize_diff.rs":"6aafcc3b42872ce9a07e0d422816f4f9e62c470b028e36bcd577c140d5017888","src/matcher_support/zipped_iterator.rs":"2c022b163bb1defda1522df9ff6824c0db0044e9314cdd45dfe39e2e1e335b96","src/matchers/all_matcher.rs":"b9534e5d1657791b341b411989b5ceba03a48ad625afc208a4290c04a5a3c3a7","src/matchers/any_matcher.rs":"1b082cd64719aad9f01b3862ff87b20cb41ccd64c268f18ab67e9a5207f6ed0c","src/matchers/anything_matcher.rs":"1bcde76add015ff9edb69b6ea55eafd8d3e34b61f56efd146fad748652d974f3","src/matchers/bool_matcher.rs":"abe9fd9f0afa6b0276e62e45d19e6c030a3e9fe4e162506ad77a3a815062d3d0","src/matchers/char_count_matcher.rs":"93ffefefa301e1968cd0cb166e451fba1662d684a682f66229cebb3c01e78c16","src/matchers/conjunction_matcher.rs":"15761c0a4cbb2f97935088f58fc00b659f1a2c12f36a22958ac96c203e16e5f1","src/matchers/container_eq_matcher.rs":"03efddf8e882415f9734721c6fb4b507f60d376088594be98c684cb913caca0a","src/matchers/contains_matcher.rs":"012416f5634b0d1c72fb529983fc9a7d42f70d652bc786402853ec40f1675845","src/matchers/contains_regex_matcher.rs":"04aa3ec505c57ed5cebbcbd5ae8341d3e45ed0dd567ba401dc8e63f1c65aab8d","src/matchers/derefs_to_matcher.rs":"3d2f6870fcdc52c4619914a04dcf4d4bf94e28f23287f06091088e4a00512670","src/matchers/disjunction_matcher.rs":"193b85d9ba177af4592e40be36cc67bdb4d2d4ce561d9f9bdc8f8e196a11cc05","src/matchers/display_matcher.rs":"f6b7216ec2c6f6200c4493e874d73c19a921b65381f9dbf147c689454ed5c87d","src/matchers/each_matcher.rs":"5cd5d03983475db8c93ef89781e3ab3e68fad81fefd829eeedc96be94bbae4e9","src/matchers/elements_are_matcher.rs":"6e593c19c7ae1c60f5e8a811ac2a5a48edd21d72e788af69fe588fbfd6caf110","src/matchers/empty_matcher.rs":"e5dc9e0e7b2799b14590b2d93ffee056339b0befa9acaf4a17aa15799e8d5716","src/matchers/eq_matcher.rs":"59f3839dc3ba5928c784fbc72c79d90d41a23ac14b27de6d8f3d50eaa2cec866","src/matchers/err_matcher.rs":"eef9b9bdbe342903c4545d7696e52d7b06df6677a91a59274fc16999ee5f658e","src/matchers/field_matcher.rs":"db9c32306aad1010dfe9c6acc96acf867a22ff8324bf560ace44f680e07d174b","src/matchers/ge_matcher.rs":"6fb61cbfb8f3e81f71900df9b0420ae155c0a19591cc95ecb024481d9047299f","src/matchers/gt_matcher.rs":"9555f0c294def104eb738de55c63f012267375e4db269cf5ca60ff5ea8d56275","src/matchers/has_entry_matcher.rs":"20bf4c271ff1d3673393d6f8c00ccce4d1e0f1604a16055f0c46bc0e5b793ddd","src/matchers/is_encoded_string_matcher.rs":"f4b60009696d1396029d36ae187fcc3528a661e1b5c38479fac9f4e9e416dd4a","src/matchers/is_matcher.rs":"df8fc55cec747b9764bae05595a403dac253514a21fccd98ea7c46a83e541999","src/matchers/is_nan_matcher.rs":"111cf9e8aca5d709942909e41fa0f6578418bc890dfeba52f9102038c5633283","src/matchers/le_matcher.rs":"3e151e3d13f944d81620aca4311dd15b382dc575266638df09eeb0e66cc674e0","src/matchers/len_matcher.rs":"24adda42e1750d8bb49ffe12a3f431c6c173c27c313d973b7eea31dd616a1642","src/matchers/lt_matcher.rs":"ff7ce1397b713955d643debc6200dc0bd5bfc4f8252433ea1650f105886ae8ef","src/matchers/matches_pattern.rs":"5c36c3f0eecded409b7fb336013a84db00176f861b65f07dc82f207570feadba","src/matchers/matches_regex_matcher.rs":"ebd8e88cfeedf394bc2550137d67dc0e15759565d49f4e3ce2e7f59c9f24808d","src/matchers/mod.rs":"fa62f807cc57cc5ee784f630e75b7447079ff5ae7513e2f15086dd061428f2ad","src/matchers/near_matcher.rs":"1e009423fc9326a984558db4efaee53d5de4acb820ef475c72c4696ed074a99b","src/matchers/none_matcher.rs":"fa403c8c7868fb41005042dfd874d5d096c52590bdd252ec5be990225f36b6ff","src/matchers/not_matcher.rs":"c98ce0362a9d7ca308b3bed4b7853d7320b22478185d777cc09fd0c627010ea0","src/matchers/ok_matcher.rs":"cadab709d5c52f2219bf59118a0a29dba8614532ead56847490e489634e5d147","src/matchers/points_to_matcher.rs":"7e845a3fa699be23e917dd90bafe4d68bbdc8d7c3fc042ba689277e3f7c8ec4a","src/matchers/pointwise_matcher.rs":"ecccadc3baa9afeacc6242e41501b27cb1581dd1502aeb127ff1fbf52cc62338","src/matchers/predicate_matcher.rs":"b9d64d86e7ef292d57aa2a62bab7ccc64b0fec262fe0bd73d08924e203d784f5","src/matchers/property_matcher.rs":"1f4fda841361381145c2c96c56cc2d50f4af008c169bfec6615b77e4b6886722","src/matchers/result_of_matcher.rs":"0d0f66f4f5d4e76062e0d427e6f7349cceb7a574d3097eb5ac982f16c311f848","src/matchers/some_matcher.rs":"6179779b798ee60e4fca6ded4330206a398798656f3a6ecb00f44cb7e81d39d0","src/matchers/str_matcher.rs":"1aedc67db3a0d0197a32f52f583e40bc89a95cca1dd70513fadecc24a7733346","src/matchers/subset_of_matcher.rs":"1a463f7c277f31e7f07fb6ef3901ad4e6d3907cb8725f2d8ec4647a4e5a8ffd8","src/matchers/superset_of_matcher.rs":"5b40ed602c8aabd66136f932aac1f5c031b99e1d3c0adcf81a50b998e1bcfde7","src/matchers/tuple_matcher.rs":"6dc905a4ccf5748f855868d2f5819dd3b92d5a4934d2639a2a3e3f6e40050b70","src/matchers/unordered_elements_are_matcher.rs":"1b5508b3aacc164ad9276834579d75baa241504f26d591995c04e51959daad01","tests/all_matcher_test.rs":"a1347ab53409098f3ea78068a2523e2f6885c22d988d50131e32e1c21f96f7a9","tests/any_matcher_test.rs":"709ca1a20c82fcaf6be821d4b328506f17855e5ffbe21996f27d67c3ae96af04","tests/assertions_test.rs":"c6e952cfed6998b6ecc7d4260d7f6c3b3a29f031856e6e4683a223a0cdf392b4","tests/colorized_diff_test.rs":"a55aa6e001df9fdc8684136a59555bf0ec27ae84848aa4c9a3a31842934b30f7","tests/composition_test.rs":"51ff2e05d3b4dd06e246eb9e4cfb3c1997369ca9cbb34ca2a7d1d5599a9900c0","tests/elements_are_matcher_test.rs":"c98dd588a184b118afd71767b3f07dfc8299ad8a9147e0835e33906a43a41552","tests/field_matcher_test.rs":"634fe967a801bc077f477ccb38649580011c2957b0f37e06a73f5654b46a7211","tests/fmt_test.rs":"67c0032bc3b3d2e07aa74c87bc583c85cba477840f1782358776f5aa33c0e3f4","tests/lib.rs":"357bbd54eb3dc3af000e2f3eaecafdc0a59a1fca30445773b64872c050870657","tests/matches_pattern_test.rs":"390038894798d08feed6dd2209427d8729dd9029ab95dd685190bc24de28364a","tests/no_color_test.rs":"f7472284fc864d4654d4677fa44274825f9286f696228c59175ee6525a8a5343","tests/pointwise_matcher_test.rs":"159bc32c50b792e9c96327c970ea44ae895e842a5f92fe817ca4d8f1e344bbc2","tests/property_matcher_test.rs":"6f2afd05c0a55624c3ef6e0f30398cb49188690956351c9a2f2e6f428874f381","tests/proptest_integration_test.rs":"0f75081bd6bfd89b8a5e430238a9da78a2f13ce6ccc4e105e32705596e9c3a7f","tests/tuple_matcher_test.rs":"3cf3a362ec753ba2e0f48db6716f32d31521335ed66a2d27aef69a271f5a902a","tests/unordered_elements_are_matcher_test.rs":"38c4f8711a056284afa0d3af3ada3a216d1b58ace71b3d1d4b844277fca45217"}}
\ No newline at end of file
diff --git a/crates/googletest/.cargo-checksum.json b/crates/googletest/.cargo-checksum.json
index 4b4a4f0..9ceb8e9 100644
--- a/crates/googletest/.cargo-checksum.json
+++ b/crates/googletest/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"81f8a9a3e6798d1747bcbdd240617b84f26d5a5c88ab748152f6a7ce792e3ef0","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","README.md":"15ead96c81651817d7b9d02c697aaedf0909a80dba2d0acb5c173c9d1d98d3e3","crate_docs.md":"4fbb126bcf704aec6e587348c5683030f28516baf1924e37f0eb4908e09a79ee","src/assertions.rs":"6bc4c890ce36a54838049eb39b35807009fc103ed2f07980e76606c8f0a417b3","src/description.rs":"b51af6bfe83fe8d41837fea2d6a8eb9efd0966df8384228aa86cccd444fd688b","src/internal/description_renderer.rs":"df583f7096cacd4e63eb720e18bf17c7f5e45eb81c57bfcffc04711abce5add8","src/internal/mod.rs":"bb5bb3dd31ccd33f893c79f81e4f629186d1047935036d773a2d5cee3feee959","src/internal/source_location.rs":"ae5014cfea179d3a619714a64810bb8feeb7e8c9267cc8c9066cc52620762b46","src/internal/test_outcome.rs":"9081c258ba3bad2f1222a006a0cfc6268b6c06fdd998e8d51f1ee6c2079900e1","src/lib.rs":"523c79fb0da7a59b0146456c919db2c20cfddda5bb03c7494fb70dd454d67390","src/matcher.rs":"6eaeec6b6d90b4ae74d298a861c03e985147f194c0c6ac296948b479d4721414","src/matcher_support/count_elements.rs":"9e9b1c238c84fb65319981267842c794f406a696895cfd84da463e050e768c23","src/matcher_support/edit_distance.rs":"72c03abebc181da092c40853e7a7e6a9d78a80074faca188bdfd3f80ef5715c2","src/matcher_support/mod.rs":"acdd182e13ecbdaa55f66526bd18ca4be826e3fa2ab9ea593d10d4a867aa12eb","src/matcher_support/summarize_diff.rs":"a27afa050f0e0b38a5813cd109f0a56152ac1552cd7400866de27f4622ba153d","src/matcher_support/zipped_iterator.rs":"ac1a1a54f866f977255403a4f7e7613a4116af163326029c54b65ca1ac57b538","src/matchers/all_matcher.rs":"b3cdaec8ffa2bb842ec8b2a9ae37890f88b00d48708c0d37584293d5d5f7ad36","src/matchers/any_matcher.rs":"c08c104699a7e7b0ad3788c1e786b15626253707de33671908cadef34f8b43e8","src/matchers/anything_matcher.rs":"d0244d3338bb8a6b9c677b1c5bcb56b56d6736f762b68110e14317f3b67c273f","src/matchers/char_count_matcher.rs":"eb89c497c73b7ef94d2cc433da0f1bdb6fd97e79fb137e7b2e3c9fd6758899ba","src/matchers/conjunction_matcher.rs":"fa7bacd6ef0b1b5fd858bb8ab48de82d09a3d4bfba1a314e5c00e35ee858e46a","src/matchers/container_eq_matcher.rs":"9b74345b3e244a574048928a3547ea00ae9dd75622feb3d5a7139c13d2c8e60b","src/matchers/contains_matcher.rs":"418c2da2366ea315bf75e610323faedf2ab6dc0d9d424cd650be691f811a384d","src/matchers/contains_regex_matcher.rs":"d6defb887ef498f4d7a9958a313927375aed996d78629e7213dffe07e2719b1c","src/matchers/disjunction_matcher.rs":"18d1918f30d58c19a9991caa0ec0cb43f55d196824373e80735a7c50ef6781c8","src/matchers/display_matcher.rs":"6ab9ddcb28173f5959d5c82dea9cb9f779f260cae2995e5f318972775317dacd","src/matchers/each_matcher.rs":"218eeb263cb1683c6a20ce80d0f127a1659a6f5da3f74bb1e3d810ab259b80c9","src/matchers/elements_are_matcher.rs":"55c890a72026eb8bc717bb0d5039b98e7f8be09da1a3c46b5a7505f63a76bb2f","src/matchers/empty_matcher.rs":"a6accac5537e29f313356b46d344354ceddd89ab9857a52febc8005b0fb5fb21","src/matchers/eq_deref_of_matcher.rs":"762038bd3e8912fe064504ca173b988bb65e3792b9d935f086cbcfba67ce346f","src/matchers/eq_matcher.rs":"fa03d1a053c6d5b0bdecfc9515e354037d9fef64cd6d2c96dc04d2ef7eb64d71","src/matchers/err_matcher.rs":"4aef1b1a5fc33825d39e4ff052dfeef7996d02eda5f931c59114125ec40ebc62","src/matchers/field_matcher.rs":"f18cc28b683beb179bbf3956ffc86852402fae96ff3fa0936ba89ba8c5f7ea65","src/matchers/ge_matcher.rs":"4c67bbdb9e4acb95f3b2b38dec4d6793781330eeb0895f3edb305215d2bcf4a1","src/matchers/gt_matcher.rs":"d557a138e30b47c9cc65d26739df3d9698ad180898cabbfa18d9a0db6474c53e","src/matchers/has_entry_matcher.rs":"a39375c7a7a12daf5080980a07564f4eaf39abe143d2bc40f64f5942b89223da","src/matchers/is_encoded_string_matcher.rs":"91f1c2e89377722248346c9cb3e87cec96e9bdec66f1a391332263ec86f955cf","src/matchers/is_matcher.rs":"6026f6f7dc9c08e65c498cbd73ea47613e65ed3e13a8dcaa0b303c54ef4bbf3a","src/matchers/is_nan_matcher.rs":"4c0d2b507e9a61ccdaaa342698cf48ed31e3d62a14b2eaa07afb95de4afe074f","src/matchers/le_matcher.rs":"6a168eca823bac8e7bb82f9d5392774fd1e0d9616cedd43c67fcdda46250a964","src/matchers/len_matcher.rs":"b77b7a4685e81ff79fceea5f382d9274e2cb8bae2abaf4f2f69ca6af4a0ceb00","src/matchers/lt_matcher.rs":"12535f0c0edad7098869fa343c225aaeac8417d97c665ff58ae917fe8497c5fe","src/matchers/matches_pattern.rs":"7d655dd989035ecdadf6cd8d59f3057bc1f115ffb162517423d6588c56ff20a8","src/matchers/matches_regex_matcher.rs":"edb52ef557ced3af81f38908917417eb08c917c166bf6fe59ab8223b53de0050","src/matchers/mod.rs":"1213c95b199519d0b978198cbfbe3239d02ade145cc6ab18a5bb91c7141b4fc1","src/matchers/near_matcher.rs":"127cfe0cc9157079a28025dd0ceceb7dea8329566e46995f4b8d1de19bc92a5e","src/matchers/none_matcher.rs":"2d9908719fcc59e281207e69c3528c8133380661b361bd159cb25e28917789ca","src/matchers/not_matcher.rs":"bf2057c6cf7b21964de766ea16d8902e6f0174793ecb602c26d39cdae5761e8e","src/matchers/ok_matcher.rs":"a05c8eb9addfb3af57a9b0b8fd56f4a966ce927be5be265b8978e19c240e145b","src/matchers/points_to_matcher.rs":"5cf3f776756b904d66847ddd49b455681266ae32c37de7eb3de729a66e1055a9","src/matchers/pointwise_matcher.rs":"0d8f9ddeacc13dcc6f34b0a4fdb7c0a5ff54f3b4b5e85f061c7907b9a2390f9b","src/matchers/predicate_matcher.rs":"d23b67acecbdd5a3bb410c8444f376947ceada2c45fd2d5e775c2e6cdcab407d","src/matchers/property_matcher.rs":"21ac721ad28ed02bc21c7d86b8fde371e1dc0606c5449b6e9aeb2c20cb0aa47f","src/matchers/some_matcher.rs":"654478b0966b9c39d5b07367b595925065cce2580c8bfec0789d5d01eb44fa41","src/matchers/str_matcher.rs":"9e24f276cc060d5174a78b862ceba65452669810cbb64505382c13395e289c10","src/matchers/subset_of_matcher.rs":"6c5f875784ae05d26adfb7532ee5d089d3de6e6369abca6b1a84f6c4aa5977a9","src/matchers/superset_of_matcher.rs":"05e6b4f965f033071fb5feec2e0d93058461b273d4613f338bd4124d59635cb7","src/matchers/tuple_matcher.rs":"d028f7e2b54264256eebc05f54891798f769d06be72572672292d77c2b6a2726","src/matchers/unordered_elements_are_matcher.rs":"ad0cc9f229fd97d689fce3b93f809260f568487d1ba17c4c8c4aa0640ede9000","tests/all_matcher_test.rs":"4b0690e97dfb426f336266bb2b09f19dd3f86504f400e89f5821a888a71e41d1","tests/any_matcher_test.rs":"064343665458ee48502697a46b14059b1971f972bcb9e92db09638b5f053505d","tests/colorized_diff_test.rs":"39227cb13b2914c14231a998bba4243309645e8f029c3c62f2a2969d699ab9a7","tests/composition_test.rs":"729dd849a7a423a000324d308874aa89558b0be6027bf4f608224a63a90322b2","tests/elements_are_matcher_test.rs":"9b2926f2f6efcd0a96f13f1f370e040c7307b2da3006f3c68a160776c5057729","tests/field_matcher_test.rs":"bc34936df7c3c4b36e1b9998565c8363967f7eeb5bae0afe4001cc0fbc36f5e9","tests/lib.rs":"506b8b37c82a7ee08ef9e8c29437b98c46097d96ae52bf75ba3c82ac24096996","tests/matches_pattern_test.rs":"8372b8823edb0028f3ed38fc9b669fcc3ad36bfa153e7671e3f6cbc8bd7798da","tests/no_color_test.rs":"3e248b14597b2b6c2b60a52f7457d822b8a19e8f5604704aa0175fd0e21069cc","tests/pointwise_matcher_test.rs":"5078a462bafe11ed27501ba789d73e4e910c2e128197c0069e1aa790cdeb2684","tests/property_matcher_test.rs":"674217ce981e7be12f569fe78279abd89a3accfa81753f58aa535814887bec09","tests/proptest_integration_test.rs":"948f4d59b7f325faa522e53f3c8a4542be349f368cb790a39c157eb6c7b47014","tests/tuple_matcher_test.rs":"2f87305c29a4692f25a481f2a30e79a765579fd538872f57669b48ca576204f6","tests/unordered_elements_are_matcher_test.rs":"1026cefc59daa8fe2aa249b87800238699d2352224c04ac4edc648b7052a13ef"},"package":"ee44a233e2e661240ef77ed5de92ea9ecf70d4832963a8582a6681e1517f3673"}
\ No newline at end of file
+{"files":{"Cargo.toml":"e00cc51a8d859b2a8fd95e199c13db5e16ed79837573b9f445e086e255d43893","LICENSE":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30","README.md":"12cf77cf9d98dab80416f8135448cae5d0bb532e9b69c63b951d49861d8d649a","crate_docs.md":"b25c57c437bd7974921b9f7172e868d3efd3b1551addc8c47f8ff6a4fc5d8ee1","src/assertions.rs":"43883536abf5f4bb9c2a746593ad1f4cba811574838f700daa703f5b952b2c41","src/description.rs":"f9fb18581c36db85a7415ffa5c9f646dfafaa7594a0a4d00ab66729de5a3f3ad","src/fixtures.rs":"528672a95a3535b2f18d28b29c2ed0a9262462738984d94af74d7cda0e4cbb86","src/fmt.rs":"986f1d796eb585999dc0614c5aed3c94230c0ba19893d2b2cf8eec90e20a38c8","src/internal/description_renderer.rs":"c7d685ed7df27e8670ec77ba7265dd220da84056e467fb62e8faf3f5e0898e8b","src/internal/mod.rs":"b07f41cae5b2b78806be89fc48da938fbe41277c37456d79891496a26e2c921f","src/internal/test_outcome.rs":"40f26fdde8a101242d376a4ffa1157fe177a017d0d4659bc747eae93762f83f3","src/lib.rs":"07bb924c81b725635a2df75b0283a3d581a92cf544923a6d6a869fce491cb4b4","src/matcher.rs":"a07acfabdc658cf23df089763c9faec77ab586cc9e153c01178cd0c9853628c6","src/matcher_support/auto_eq.rs":"e7a95ffb7ebe4f23986920e494e89ac254c596c43382bdb730a17eda02b83d87","src/matcher_support/count_elements.rs":"34ac45d3fedef1e9ff9ea46144e450fc2972e17e3d266699c61fdf68cd5ed2b1","src/matcher_support/edit_distance.rs":"e91a22eb46f1000a0ef2efac547ab1a05a18612d1c5024400800a0413ea8c74e","src/matcher_support/mod.rs":"df976626c666df50f9a12ab4486fb64c327ef9b7ee3b497d89e9a7aada889c1e","src/matcher_support/summarize_diff.rs":"82700a07cb45c20d6375227928f32e17422486373bc10377d4110537bfb271a0","src/matcher_support/zipped_iterator.rs":"ac1a1a54f866f977255403a4f7e7613a4116af163326029c54b65ca1ac57b538","src/matchers/all_matcher.rs":"a12ea32a2872ba6e0194c4b294182157ea1fcd8dd8800005c45f9faad81aa6ba","src/matchers/any_matcher.rs":"2d2b1db30790c7dadbd7e1b77e1c796b71e3770480d21e15bce7033eeae0ecb2","src/matchers/anything_matcher.rs":"5905d9ae3e01274ec514cb5f09518dc7bfd416fc0bd57b02ee10d3ead04ce7bf","src/matchers/bool_matcher.rs":"245a72604474071a00f6a4231a00d1c984aca035cc9d79c0aba090eb46d686b1","src/matchers/char_count_matcher.rs":"fe013c27c9e3a7e8b627472c272dc38fa0ab0674186384bc53ce64130821ae38","src/matchers/conjunction_matcher.rs":"687e6f1c5c8d33cec539440626790c99fb9048ca95b3e5d80f9fa531caa8674f","src/matchers/container_eq_matcher.rs":"b502076dde050afd270848c9669b0818f8f749e5afabc81441f800defdc94562","src/matchers/contains_matcher.rs":"f79b8f816a265ddb96a769e9ac48f8a6b175f127a4b1d8b24e8f4d76fca239c0","src/matchers/contains_regex_matcher.rs":"4b9e13c36ef4314ee7adbbc46e5138ebdb1fbc41f231bc9cdaad8efdb3f8cc5e","src/matchers/derefs_to_matcher.rs":"0f91f73cd0fcdb580be617f11b96b93d1055e8871554f745c76175b035e20621","src/matchers/disjunction_matcher.rs":"45f1c1fb5192f6cfd062eb05e77135b566763292589d8fe29048aaa01b357698","src/matchers/display_matcher.rs":"040296c9ad47f0dab694a7c3b3b24beecd4e02d2de011e52d432309ba97e7e02","src/matchers/each_matcher.rs":"61802a11497447a8f8144eb7be97b7600cc04fa55ed884728c4d7c14d0ca2455","src/matchers/elements_are_matcher.rs":"feccfb18fa552d94d25ca051bd9b4fe69d65004604b2ec317699f9c1bb4127ef","src/matchers/empty_matcher.rs":"7d68972d24e7d81dea6bdc77a8193d0d8c9b1988aab737ca3721b75e134b8e50","src/matchers/eq_matcher.rs":"8ea401d1e4ad5ff2f96a7178640c7d7e3b416a43e4d88086460ac4fba23e7044","src/matchers/err_matcher.rs":"0f8c9e47b76973393817d760c1b3833e4f81fd4a1cbe0bdf1237947b18c64902","src/matchers/field_matcher.rs":"01c95cca86e69266146a4331f00e2d393f88856a5aa44c2163ae9cc5e1ab348d","src/matchers/ge_matcher.rs":"cc45fae8414095959f5da830589495b0a1623666410953b027324863f8883fb0","src/matchers/gt_matcher.rs":"4c1fd646ac87f7525b3d080e78c6abf935bed50221e4dfd4339e8b75935af115","src/matchers/has_entry_matcher.rs":"3b43e0e6a24890a94910be35f0fd0c25fac602fa660d44303d769f7bd00145be","src/matchers/is_encoded_string_matcher.rs":"6519057f21db928c6dbb4d62eb7d695e992df1d660a16dbabd0c7bb73fb65113","src/matchers/is_matcher.rs":"4e842d665eb710540fbf7b274755d665727ff01f51bee57e9b2697f6aec90cd7","src/matchers/is_nan_matcher.rs":"9972b43fd35c47b89755d8ec176631f76b6cfec725d8972caed277369fc74493","src/matchers/le_matcher.rs":"108581b0fb4d9141e5aa0ec3abb640bd2828b5e39b0f3793bd767cba4eca3afb","src/matchers/len_matcher.rs":"f7312f738fc644ba942a15b017285cfc67a2458c815d5ce29e56cca307b6f357","src/matchers/lt_matcher.rs":"5966f449669160e2905acc0da5db5487d6da580bc96a6e77fb320eb8d4546990","src/matchers/matches_pattern.rs":"9895407b21823da2608be95086605f6bd508510bc987f1a840a495025b574921","src/matchers/matches_regex_matcher.rs":"5fca7eaddbe064c5053a0ebbd9532f8e00ab263101afb3948716fad4aaa0c38f","src/matchers/mod.rs":"0c0d9ca73488df9f9bed475da9d0b60ca69a5d16daf534fef77dd04d40d0290b","src/matchers/near_matcher.rs":"3ec423d88e9d52b518c9892b8271e76789971160c1f28534bce8adc250be5701","src/matchers/none_matcher.rs":"d422658c824f07afe870003383d42b090d9a7a72d539450e7903252a859352b1","src/matchers/not_matcher.rs":"140e0959a25cca75fd3aa41fad0dd71d50753b6b38220ff4a4960f2ef7d295e0","src/matchers/ok_matcher.rs":"2aae736e349bddc84b4cee5699b2e6d21817facdc49a28415a8ff919adea63bb","src/matchers/points_to_matcher.rs":"a1f55e8d9813a3651cc24307923b461f398f761b4a040d00faac623bfffeef82","src/matchers/pointwise_matcher.rs":"3b2ed83dcd216c82063697e03bad998e9c6f2d93f0c18c8edb847b1ceeea2ff4","src/matchers/predicate_matcher.rs":"825896ee6839f9b6a39f971529fc3e33d47b6f4abece86eaf911aff2af2e795e","src/matchers/property_matcher.rs":"d65d1bd8059cead41ce81db7c080ec639cf89e8d5846939b34687a1611494b24","src/matchers/result_of_matcher.rs":"fbcb266f7a65921fa799795fef21ab684706cf6b4ab79c2eb09c10c225497b8f","src/matchers/some_matcher.rs":"9ee7282180400476c39e7f43d5f028e93442423142665ecd23c397e5bd40c935","src/matchers/str_matcher.rs":"a10443574eec5c5e33c93e6208651a5f0b865a9a200517bf2e4d1f12c792a424","src/matchers/subset_of_matcher.rs":"e0966351bff9623aa63aaf7ae9403e05e6aabcdd8f4bb9074b19069cb0adff6b","src/matchers/superset_of_matcher.rs":"fd5af81f7eb390e6b6fc1c61b79963d3220d6505615171737b566e06eb441e9e","src/matchers/tuple_matcher.rs":"d00eb6fecd1d5671418e3bbe62c67f422b6a945ee1276e16edc1402c8e4f2edb","src/matchers/unordered_elements_are_matcher.rs":"fed9b464ee1597737a1ed86cd5fa0aeeb566fbd0ef3b00cc839988c3c2eb3dbc","tests/all_matcher_test.rs":"1808617e4f59b9e7ad001c45d5653d5389e761347ae4194f221767e5e6b5b7ab","tests/any_matcher_test.rs":"0ce6aa6de318e0e1f0976a04b1581f012001a9afdc1474c3a460e844e8b4f97e","tests/assertions_test.rs":"3425e5fc54a13034069305a9c30ee5fe7b9c5766a2a75d5dca721a3456b08211","tests/colorized_diff_test.rs":"01e9b2fe99aced6aad0281ba18f2442cf036e654b5fbf6c937dd957572e3ab95","tests/composition_test.rs":"bda4157c7a68001eddaabba788c72ce2db86645225cf47a105661799346eced7","tests/elements_are_matcher_test.rs":"decdfa0388dc76703ef2105fceea2bb70cc1e01c04a27208f8e68e8d6fc069af","tests/field_matcher_test.rs":"f82c36f3662e8798b7eaa7dec04b936fac2e74692cdbb90c305ae7ae41347254","tests/fmt_test.rs":"56796a45c9666bc08c7a88e62bdab9ed98b6f1d7adf0727e85c9c5bf2ebd7c31","tests/lib.rs":"e783f3e4908cb080721bdaf72ab4b6e1fb2165b5f4fb0f4baa179ad5c06dfe0f","tests/matches_pattern_test.rs":"bd5dfe2019828ae1df891f88d487bc64bb1a8ccf086fa05b28d4ddd2a785cfb5","tests/no_color_test.rs":"1bbca449ae5bdc261c96879bf5298e60b589947634bc6f8b9bdcbefdd13241a3","tests/pointwise_matcher_test.rs":"cf3e0c13c32a5e891fa47fe765e744a8d1652acc783cac9b4f0e501562df0877","tests/property_matcher_test.rs":"4461d54abaaa5135e371c516ce9b22c4b1bd187d25fea18d96562adaaf8a2839","tests/proptest_integration_test.rs":"948f4d59b7f325faa522e53f3c8a4542be349f368cb790a39c157eb6c7b47014","tests/tuple_matcher_test.rs":"cfa703ef99a2cafedb5cec8d8920fe2d146f17b0e9e1d894d473083d9d408d65","tests/unordered_elements_are_matcher_test.rs":"2fa8dbdd170567b8d02f46427b1ad5533a3f1737e0adb4fe6340b3f3dba110c7"},"package":"dce026f84cdd339bf71be01b24fe67470ee634282f68c1c4b563d00a9f002b05"}
\ No newline at end of file
diff --git a/crates/googletest/Android.bp b/crates/googletest/Android.bp
index bd6d8f8..4ebc3a8 100644
--- a/crates/googletest/Android.bp
+++ b/crates/googletest/Android.bp
@@ -18,7 +18,7 @@
host_supported: true,
crate_name: "googletest",
cargo_env_compat: true,
- cargo_pkg_version: "0.11.0",
+ cargo_pkg_version: "0.13.0",
crate_root: "src/lib.rs",
edition: "2021",
rustlibs: [
diff --git a/crates/googletest/Cargo.toml b/crates/googletest/Cargo.toml
index ed9d01c..b1d26ae 100644
--- a/crates/googletest/Cargo.toml
+++ b/crates/googletest/Cargo.toml
@@ -11,15 +11,20 @@
[package]
edition = "2021"
-rust-version = "1.66.0"
+rust-version = "1.70.0"
name = "googletest"
-version = "0.11.0"
+version = "0.13.0"
authors = [
"Bradford Hovinen <hovinen@google.com>",
"Bastien Jacot-Guillarmod <bjacotg@google.com>",
"Maciej Pietrzak <mpi@google.com>",
"Martin Geisler <mgeisler@google.com>",
]
+build = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
description = "A rich assertion and matcher library inspired by GoogleTest for C++"
readme = "README.md"
keywords = [
@@ -35,12 +40,80 @@
license = "Apache-2.0"
repository = "https://github.com/google/googletest-rust"
+[lib]
+name = "googletest"
+path = "src/lib.rs"
+
+[[test]]
+name = "all_matcher_test"
+path = "tests/all_matcher_test.rs"
+
+[[test]]
+name = "any_matcher_test"
+path = "tests/any_matcher_test.rs"
+
+[[test]]
+name = "assertions_test"
+path = "tests/assertions_test.rs"
+
+[[test]]
+name = "colorized_diff_test"
+path = "tests/colorized_diff_test.rs"
+
+[[test]]
+name = "composition_test"
+path = "tests/composition_test.rs"
+
+[[test]]
+name = "elements_are_matcher_test"
+path = "tests/elements_are_matcher_test.rs"
+
+[[test]]
+name = "field_matcher_test"
+path = "tests/field_matcher_test.rs"
+
+[[test]]
+name = "fmt_test"
+path = "tests/fmt_test.rs"
+
+[[test]]
+name = "lib"
+path = "tests/lib.rs"
+
+[[test]]
+name = "matches_pattern_test"
+path = "tests/matches_pattern_test.rs"
+
+[[test]]
+name = "no_color_test"
+path = "tests/no_color_test.rs"
+
+[[test]]
+name = "pointwise_matcher_test"
+path = "tests/pointwise_matcher_test.rs"
+
+[[test]]
+name = "property_matcher_test"
+path = "tests/property_matcher_test.rs"
+
+[[test]]
+name = "proptest_integration_test"
+path = "tests/proptest_integration_test.rs"
+
+[[test]]
+name = "tuple_matcher_test"
+path = "tests/tuple_matcher_test.rs"
+
+[[test]]
+name = "unordered_elements_are_matcher_test"
+path = "tests/unordered_elements_are_matcher_test.rs"
+
[dependencies.anyhow]
version = "1"
optional = true
[dependencies.googletest_macro]
-version = "0.11.0"
+version = "0.13.0"
[dependencies.num-traits]
version = "0.2.17"
@@ -60,6 +133,3 @@
[dev-dependencies.quickcheck]
version = "1.0.3"
-
-[dev-dependencies.serial_test]
-version = "2.0.0"
diff --git a/crates/googletest/METADATA b/crates/googletest/METADATA
index 8b0b3e2..4b1ad07 100644
--- a/crates/googletest/METADATA
+++ b/crates/googletest/METADATA
@@ -1,17 +1,17 @@
name: "googletest"
description: "A rich assertion and matcher library inspired by GoogleTest for C++"
third_party {
- version: "0.11.0"
+ version: "0.13.0"
license_type: NOTICE
last_upgrade_date {
- year: 2024
- month: 2
- day: 2
+ year: 2025
+ month: 1
+ day: 3
}
homepage: "https://crates.io/crates/googletest"
identifier {
type: "Archive"
- value: "https://static.crates.io/crates/googletest/googletest-0.11.0.crate"
- version: "0.11.0"
+ value: "https://static.crates.io/crates/googletest/googletest-0.13.0.crate"
+ version: "0.13.0"
}
}
diff --git a/crates/googletest/README.md b/crates/googletest/README.md
index d442770..c611e79 100644
--- a/crates/googletest/README.md
+++ b/crates/googletest/README.md
@@ -25,7 +25,7 @@
* A new set of assertion macros offering similar functionality to those of
[GoogleTest](https://google.github.io/googletest/primer.html#assertions).
-**The minimum supported Rust version is 1.66**.
+**The minimum supported Rust version is 1.70**.
> :warning: The API is not fully stable and may still be changed until we
> publish version 1.0.
@@ -34,6 +34,15 @@
> not be used directly. Those items or modules are only for internal uses and
> their API may change without a major version update.
+## Learning resources
+
+If you're just getting started with `googletest`, consider going through
+the first chapter of
+["Advanced testing for Rust applications"](https://github.com/mainmatter/rust-advanced-testing-workshop),
+a self-guided Rust course: it provides a guided introduction to the library,
+with exercises to help you get comfortable with `googletest` macros,
+its matchers and its overall philosophy.
+
## Assertions and matchers
The core of GoogleTest is its *matchers*. Matchers indicate what aspect of an
@@ -45,7 +54,7 @@
* [`assert_that!`] panics if the assertion fails, aborting the test.
* [`expect_that!`] logs an assertion failure, marking the test as having
failed, but allows the test to continue running (called a _non-fatal
- assertion_). It requires the use of the [`googletest::test`] attribute macro
+ assertion_). It requires the use of the [`gtest`] attribute macro
on the test itself.
* [`verify_that!`] has no side effects and evaluates to a [`Result<()>`] whose
`Err` variant describes the assertion failure, if there is one. In
@@ -65,7 +74,7 @@
assert_that!(value, eq(4));
}
-#[googletest::test]
+#[gtest]
fn two_logged_failures() {
let value = 2;
expect_that!(value, eq(4)); // Test now failed, but continues executing.
@@ -98,7 +107,7 @@
```rust
use googletest::prelude::*;
-#[googletest::test]
+#[gtest]
fn contains_at_least_one_item_at_least_3() {
let value = vec![1, 2, 3];
expect_that!(value, contains(ge(3)));
@@ -110,7 +119,7 @@
```rust
use googletest::prelude::*;
-#[googletest::test]
+#[gtest]
fn strictly_between_9_and_11() {
let value = 10;
expect_that!(value, gt(9).and(not(ge(11))));
@@ -156,11 +165,9 @@
expected: T,
}
-impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
- type ActualT = T;
-
- fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
- (self.expected == *actual).into()
+impl<T: PartialEq + Debug + Copy> Matcher<T> for MyEqMatcher<T> {
+ fn matches(&self, actual: T) -> MatcherResult {
+ (self.expected == actual).into()
}
fn describe(&self, matcher_result: MatcherResult) -> String {
@@ -179,7 +186,7 @@
It is recommended to expose a function which constructs the matcher:
```rust
-pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<ActualT = T> {
+pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<T> {
MyEqMatcher { expected }
}
```
@@ -187,7 +194,7 @@
The new matcher can then be used in the assertion macros:
```rust
-#[googletest::test]
+#[gtest]
fn should_be_equal_by_my_definition() {
expect_that!(10, eq_my_way(10));
}
@@ -202,12 +209,12 @@
This is analogous to the `EXPECT_*` family of macros in GoogleTest.
To make a non-fatal assertion, use the macro [`expect_that!`]. The test must
-also be marked with [`googletest::test`] instead of the Rust-standard `#[test]`.
+also be marked with [`gtest`] instead of the Rust-standard `#[test]`.
```rust
use googletest::prelude::*;
-#[googletest::test]
+#[gtest]
fn three_non_fatal_assertions() {
let value = 2;
expect_that!(value, eq(2)); // Passes; test still considered passing.
@@ -222,7 +229,7 @@
```rust
use googletest::prelude::*;
-#[googletest::test]
+#[gtest]
fn failing_non_fatal_assertion() -> Result<()> {
let value = 2;
expect_that!(value, eq(3)); // Just marks the test as having failed.
@@ -235,7 +242,7 @@
```rust
use googletest::prelude::*;
-#[googletest::test]
+#[gtest]
fn failing_fatal_assertion_after_non_fatal_assertion() -> Result<()> {
let value = 2;
verify_that!(value, eq(3))?; // Fails and aborts the test.
@@ -246,12 +253,12 @@
### Interoperability
-You can use the `#[googletest::test]` macro together with many other libraries
+You can use the `#[gtest]` macro together with many other libraries
such as [rstest](https://crates.io/crates/rstest). Just apply both attribute
macros to the test:
```rust
-#[googletest::test]
+#[gtest]
#[rstest]
#[case(1)]
#[case(2)]
@@ -261,16 +268,16 @@
}
```
-Make sure to put `#[googletest::test]` *before* `#[rstest]`. Otherwise the
+Make sure to put `#[gtest]` *before* `#[rstest]`. Otherwise the
annotated test will run twice, since both macros will attempt to register a test
with the Rust test harness.
The macro also works together with
-[async tests with Tokio](https://docs.rs/tokio/latest/tokio/attr.test.html) in
+[async tests with Tokio](https://docs.rs/tokio/latest/tokio/attr.gtest.html) in
the same way:
```rust
-#[googletest::test]
+#[gtest]
#[tokio::test]
async fn should_work_with_tokio() -> Result<()> {
verify_that!(3, gt(0))
@@ -348,7 +355,7 @@
[`expect_pred!`]: https://docs.rs/googletest/*/googletest/macro.expect_pred.html
[`expect_that!`]: https://docs.rs/googletest/*/googletest/macro.expect_that.html
[`fail!`]: https://docs.rs/googletest/*/googletest/macro.fail.html
-[`googletest::test`]: https://docs.rs/googletest/*/googletest/attr.test.html
+[`gtest`]: https://docs.rs/googletest/*/googletest/attr.gtest.html
[`matches_pattern!`]: https://docs.rs/googletest/*/googletest/macro.matches_pattern.html
[`verify_pred!`]: https://docs.rs/googletest/*/googletest/macro.verify_pred.html
[`verify_that!`]: https://docs.rs/googletest/*/googletest/macro.verify_that.html
diff --git a/crates/googletest/crate_docs.md b/crates/googletest/crate_docs.md
index 8c8b47c..3a6b037 100644
--- a/crates/googletest/crate_docs.md
+++ b/crates/googletest/crate_docs.md
@@ -6,6 +6,14 @@
range of assertions on data,
* A rich set of matchers, and
* A new set of test assertion macros.
+## Learning resources
+
+If you're just getting started with `googletest`, consider going through
+the first chapter of
+["Advanced testing for Rust applications"](https://github.com/mainmatter/rust-advanced-testing-workshop),
+a self-guided Rust course: it provides a guided introduction to the library,
+with exercises to help you get comfortable with `googletest` macros,
+its matchers and its overall philosophy.
## Assertions and matchers
@@ -18,7 +26,7 @@
* [`assert_that!`] panics if the assertion fails, aborting the test.
* [`expect_that!`] logs an assertion failure, marking the test as having
failed, but allows the test to continue running (called a _non-fatal
- assertion_). It requires the use of the [`googletest::test`][crate::test]
+ assertion_). It requires the use of the [`gtest`]
attribute macro on the test itself.
* [`verify_that!`] has no side effects and evaluates to a [`Result`] whose
`Err` variant describes the assertion failure, if there is one. In
@@ -41,7 +49,7 @@
}
# /* The attribute macro would prevent the function from being compiled in a doctest.
-#[googletest::test]
+#[gtest]
# */
fn two_logged_failures() {
let value = 2;
@@ -74,25 +82,25 @@
use googletest::prelude::*;
# /* The attribute macro would prevent the function from being compiled in a doctest.
-#[googletest::test]
+#[gtest]
# */
fn contains_at_least_one_item_at_least_3() {
# googletest::internal::test_outcome::TestOutcome::init_current_test_outcome();
let value = vec![1, 2, 3];
- expect_that!(value, contains(ge(3)));
+ expect_that!(value, contains(ge(&3)));
# googletest::internal::test_outcome::TestOutcome::close_current_test_outcome::<&str>(Ok(()))
# .unwrap();
}
# contains_at_least_one_item_at_least_3();
```
-They can also be logically combined:
+They can also be logically combined, with methods from [`MatcherBase`]:
```
use googletest::prelude::*;
# /* The attribute macro would prevent the function from being compiled in a doctest.
-#[googletest::test]
+#[gtest]
# */
fn strictly_between_9_and_11() {
# googletest::internal::test_outcome::TestOutcome::init_current_test_outcome();
@@ -120,13 +128,13 @@
| [`contains_each!`] | A container containing distinct elements each of the arguments match. |
| [`contains_regex`] | A string containing a substring matching the given regular expression. |
| [`contains_substring`] | A string containing the given substring. |
+| [`derefs_to`] | A [`Deref`] which `deref()`s to a value that the argument matches. |
| [`displays_as`] | A [`Display`] value whose formatted string is matched by the argument. |
| [`each`] | A container all of whose elements the given argument matches. |
| [`elements_are!`] | A container whose elements the arguments match, in order. |
| [`empty`] | An empty collection. |
| [`ends_with`] | A string ending with the given suffix. |
| [`eq`] | A value equal to the argument, in the sense of the [`PartialEq`] trait. |
-| [`eq_deref_of`] | A value equal to the dereferenced value of the argument. |
| [`err`] | A [`Result`][std::result::Result] containing an `Err` variant the argument matches. |
| [`field!`] | A struct or enum with a given field whose value the argument matches. |
| [`ge`] | A [`PartialOrd`] value greater than or equal to the given value. |
@@ -144,7 +152,7 @@
| [`not`] | Any value the argument does not match. |
| [`ok`] | A [`Result`][std::result::Result] containing an `Ok` variant the argument matches. |
| [`pat!`] | Alias for [`matches_pattern!`]. |
-| [`points_to`] | Any [`Deref`] such as `&`, `Rc`, etc. whose value the argument matches. |
+| [`points_to`] | A reference `&` which points to a value that the argument matches. |
| [`pointwise!`] | A container whose contents the arguments match in a pointwise fashion. |
| [`predicate`] | A value on which the given predicate returns true. |
| [`some`] | An [`Option`] containing `Some` whose value the argument matches. |
@@ -164,12 +172,12 @@
[`contains_regex`]: matchers::contains_regex
[`contains_substring`]: matchers::contains_substring
[`displays_as`]: matchers::displays_as
+[`derefs_to`]: matchers::derefs_to
[`each`]: matchers::each
[`elements_are!`]: matchers::elements_are
[`empty`]: matchers::empty
[`ends_with`]: matchers::ends_with
[`eq`]: matchers::eq
-[`eq_deref_of`]: matchers::eq_deref_of
[`err`]: matchers::err
[`field!`]: matchers::field
[`ge`]: matchers::ge
@@ -205,22 +213,21 @@
## Writing matchers
One can extend the library by writing additional matchers. To do so, create
-a struct holding the matcher's data and have it implement the trait
-[`Matcher`]:
+a struct holding the matcher's data and have it implement the traits
+[`Matcher`] and [`MatcherBase`]:
```no_run
-use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
+use googletest::{description::Description, matcher::{Matcher, MatcherBase, MatcherResult}};
use std::fmt::Debug;
+#[derive(MatcherBase)]
struct MyEqMatcher<T> {
expected: T,
}
-impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
- type ActualT = T;
-
- fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
- if self.expected == *actual {
+impl<T: PartialEq + Debug + Copy> Matcher<T> for MyEqMatcher<T> {
+ fn matches(&self, actual: T) -> MatcherResult {
+ if self.expected == actual {
MatcherResult::Match
} else {
MatcherResult::NoMatch
@@ -243,18 +250,16 @@
It is recommended to expose a function which constructs the matcher:
```no_run
- # use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
+ # use googletest::{description::Description, matcher::{Matcher, MatcherBase, MatcherResult}};
# use std::fmt::Debug;
- #
+ # #[derive(MatcherBase)]
# struct MyEqMatcher<T> {
# expected: T,
# }
#
- # impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
- # type ActualT = T;
- #
- # fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
- # if self.expected == *actual {
+ # impl<T: PartialEq + Debug + Copy> Matcher<T> for MyEqMatcher<T> {
+ # fn matches(&self, actual: T) -> MatcherResult {
+ # if self.expected == actual {
# MatcherResult::Match
# } else {
# MatcherResult::NoMatch
@@ -273,7 +278,7 @@
# }
# }
#
- pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<ActualT = T> {
+ pub fn eq_my_way<T: PartialEq + Debug + Copy>(expected: T) -> impl Matcher<T> {
MyEqMatcher { expected }
}
```
@@ -282,18 +287,16 @@
```
# use googletest::prelude::*;
-# use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
+# use googletest::{description::Description, matcher::{Matcher, MatcherBase, MatcherResult}};
# use std::fmt::Debug;
-#
+# #[derive(MatcherBase)]
# struct MyEqMatcher<T> {
# expected: T,
# }
#
-# impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
-# type ActualT = T;
-#
-# fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
-# if self.expected == *actual {
+# impl<T: PartialEq + Debug + Copy> Matcher<T> for MyEqMatcher<T> {
+# fn matches(&self, actual: T) -> MatcherResult {
+# if self.expected == actual {
# MatcherResult::Match
# } else {
# MatcherResult::NoMatch
@@ -312,11 +315,11 @@
# }
# }
#
-# pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<ActualT = T> {
+# pub fn eq_my_way<T: PartialEq + Debug + Copy>(expected: T) -> impl Matcher<T> {
# MyEqMatcher { expected }
# }
# /* The attribute macro would prevent the function from being compiled in a doctest.
-#[googletest::test]
+#[gtest]
# */
fn should_be_equal_by_my_definition() {
# googletest::internal::test_outcome::TestOutcome::init_current_test_outcome();
@@ -335,13 +338,12 @@
aborts.
To make a non-fatal assertion, use the macro [`expect_that!`]. The test must
-also be marked with [`googletest::test`][crate::test] instead of the
-Rust-standard `#[test]`.
+also be marked with [`gtest`] instead of the Rust-standard `#[test]`.
```no_run
use googletest::prelude::*;
-#[googletest::test]
+#[gtest]
fn three_non_fatal_assertions() {
let value = 2;
expect_that!(value, eq(2)); // Passes; test still considered passing.
@@ -357,7 +359,7 @@
use googletest::prelude::*;
# /* Make sure this also compiles as a doctest.
-#[googletest::test]
+#[gtest]
# */
fn failing_non_fatal_assertion() -> Result<()> {
let value = 2;
@@ -371,7 +373,7 @@
```no_run
use googletest::prelude::*;
-#[googletest::test]
+#[gtest]
fn failing_fatal_assertion_after_non_fatal_assertion() -> Result<()> {
let value = 2;
expect_that!(value, eq(2)); // Passes; test still considered passing.
@@ -434,62 +436,98 @@
# always_fails().unwrap_err();
```
+## Conversion from `Result::Err` and `Option::None`
+
+To simplify error management during a test arrangement, [`Result<T>`]
+provides a few conversion utilities.
+
+If your setup function returns `std::result::Result<T, E>` where `E: std::error::Error`,
+the `std::result::Result<T, E>` can simply be handled with the `?` operator. If an `Err(e)`
+is returned, the test will report a failure at the line where the `?` operator has been
+applied (or the lowest caller without `#[track_caller]`).
+
+```
+# use googletest::prelude::*;
+struct PngImage { h: i32, w: i32 /* ... */ }
+impl PngImage {
+ fn new_from_file(file_name: &str) -> std::result::Result<Self, std::io::Error> {
+ Err(std::io::Error::new(std::io::ErrorKind::Other, "oh no!"))
+
+ }
+ fn rotate(&mut self) { std::mem::swap(&mut self.h, &mut self.w);}
+ fn dimensions(&self) -> (i32, i32) { (self.h, self.w)}
+}
+
+fn test_png_image_dimensions() -> googletest::Result<()> {
+ // Arrange
+ let mut png = PngImage::new_from_file("example.png")?;
+ verify_eq!(png.dimensions(), (128, 64))?;
+
+ // Act
+ png.rotate();
+
+ // Assert
+ expect_eq!(png.dimensions(), (64, 128));
+ Ok(())
+}
+
+# test_png_image_dimensions().unwrap_err();
+```
+
+If your setup function returns `Option<T>` or `std::result::Result<T, E>` where
+`E: !std::error::Error`, then you can convert these types with `into_test_result()`
+from the `IntoTestResult` extension trait.
+
+```
+# use googletest::prelude::*;
+# struct PngImage;
+# static PNG_BINARY: [u8;0] = [];
+
+impl PngImage {
+ fn new_from_binary(bin: &[u8]) -> std::result::Result<Self, String> {
+ Err("Parsing failed".into())
+ }
+}
+
+# /* The attribute macro would prevent the function from being compiled in a doctest.
+#[gtest]
+# */
+fn test_png_image_binary() -> googletest::Result<()> {
+ // Arrange
+ let png_image = PngImage::new_from_binary(&PNG_BINARY).into_test_result()?;
+ /* ... */
+ # Ok(())
+}
+# test_png_image_binary().unwrap_err();
+
+impl PngImage {
+ fn new_from_cache(key: u64) -> Option<Self> {
+ None
+ }
+}
+
+# /* The attribute macro would prevent the function from being compiled in a doctest.
+#[gtest]
+# */
+fn test_png_from_cache() -> googletest::Result<()> {
+ // Arrange
+ let png_image = PngImage::new_from_cache(123).into_test_result()?;
+ /* ... */
+ # Ok(())
+}
+# test_png_from_cache().unwrap_err();
+```
+
+
## Integrations with other crates
GoogleTest Rust includes integrations with the
-[Anyhow](https://crates.io/crates/anyhow) and
[Proptest](https://crates.io/crates/proptest) crates to simplify turning
-errors from those crates into test failures.
-
-To use this, activate the `anyhow`, respectively `proptest` feature in
-GoogleTest Rust and invoke the extension method [`into_test_result()`] on a
-`Result` value in your test. For example:
-
-```
-# use googletest::prelude::*;
-# #[cfg(feature = "anyhow")]
-# use anyhow::anyhow;
-# #[cfg(feature = "anyhow")]
-# /* The attribute macro would prevent the function from being compiled in a doctest.
-#[test]
-# */
-fn has_anyhow_failure() -> Result<()> {
- Ok(just_return_error().into_test_result()?)
-}
-
-# #[cfg(feature = "anyhow")]
-fn just_return_error() -> anyhow::Result<()> {
- anyhow::Result::Err(anyhow!("This is an error"))
-}
-# #[cfg(feature = "anyhow")]
-# has_anyhow_failure().unwrap_err();
-```
-
-One can convert Proptest test failures into GoogleTest test failures when the
-test is invoked with
-[`TestRunner::run`](https://docs.rs/proptest/latest/proptest/test_runner/struct.TestRunner.html#method.run):
-
-```
-# use googletest::prelude::*;
-# #[cfg(feature = "proptest")]
-# use proptest::test_runner::{Config, TestRunner};
-# #[cfg(feature = "proptest")]
-# /* The attribute macro would prevent the function from being compiled in a doctest.
-#[test]
-# */
-fn numbers_are_greater_than_zero() -> Result<()> {
- let mut runner = TestRunner::new(Config::default());
- runner.run(&(1..100i32), |v| Ok(verify_that!(v, gt(0))?)).into_test_result()
-}
-# #[cfg(feature = "proptest")]
-# numbers_are_greater_than_zero().unwrap();
-```
-
-Similarly, when the `proptest` feature is enabled, GoogleTest assertion failures
-can automatically be converted into Proptest
+GoogleTest assertion failures into Proptest
[`TestCaseError`](https://docs.rs/proptest/latest/proptest/test_runner/enum.TestCaseError.html)
-through the `?` operator as the example above shows.
+through the `?` operator.
[`and_log_failure()`]: GoogleTestSupport::and_log_failure
[`into_test_result()`]: IntoTestResult::into_test_result
[`Matcher`]: matcher::Matcher
+[`MatcherBase`]: matcher::MatcherBase
diff --git a/crates/googletest/src/assertions.rs b/crates/googletest/src/assertions.rs
index 2668028..ac5177e 100644
--- a/crates/googletest/src/assertions.rs
+++ b/crates/googletest/src/assertions.rs
@@ -120,29 +120,37 @@
/// not supported; see [Rust by Example](https://doc.rust-lang.org/rust-by-example/primitives/tuples.html#tuples).
#[macro_export]
macro_rules! verify_that {
+ // specialized to sequences:
($actual:expr, [$($expecteds:expr),+ $(,)?]) => {
- $crate::assertions::internal::check_matcher(
- &$actual,
- $crate::matchers::elements_are![$($expecteds),+],
- stringify!($actual),
- $crate::internal::source_location::SourceLocation::new(file!(), line!(), column!()),
- )
+ {
+ use $crate::assertions::internal::Subject as _;
+ $actual.check(
+ $crate::matchers::elements_are![$($expecteds),+],
+ stringify!($actual),
+ )
+ }
};
+
+ // specialized to unordered sequences:
($actual:expr, {$($expecteds:expr),+ $(,)?}) => {
- $crate::assertions::internal::check_matcher(
- &$actual,
- $crate::matchers::unordered_elements_are![$($expecteds),+],
- stringify!($actual),
- $crate::internal::source_location::SourceLocation::new(file!(), line!(), column!()),
- )
+ {
+ use $crate::assertions::internal::Subject as _;
+ $actual.check(
+ $crate::matchers::unordered_elements_are![$($expecteds),+],
+ stringify!($actual),
+ )
+ }
};
+
+ // general case:
($actual:expr, $expected:expr $(,)?) => {
- $crate::assertions::internal::check_matcher(
- &$actual,
- $expected,
- stringify!($actual),
- $crate::internal::source_location::SourceLocation::new(file!(), line!(), column!()),
- )
+ {
+ use $crate::assertions::internal::Subject as _;
+ $actual.check(
+ $expected,
+ stringify!($actual),
+ )
+ }
};
}
@@ -165,30 +173,25 @@
/// # */
/// fn test() -> Result<()> {
/// let a = 1;
-/// let b = 7;
-/// let n = 5;
-/// verify_pred!(equals_modulo(a, b, n))?;
+/// fn b(_x: i32) -> i32 { 7 }
+/// verify_pred!(equals_modulo(a, b(b(2)), 2 + 3))?;
/// Ok(())
/// }
/// # verify_that!(
/// # test(),
-/// # err(displays_as(contains_substring("equals_modulo(a, b, n) was false with")))
+/// # err(displays_as(contains_substring("equals_modulo(a, b(b(2)), 2 + 3) was false with")))
/// # ).unwrap();
/// ```
///
/// This results in the following message:
///
/// ```text
-/// equals_modulo(a, b, n) was false with
+/// equals_modulo(a, b(b(2)), 2 + 3) was false with
/// a = 1,
-/// b = 7,
-/// n = 5
+/// b(b(2)) = 7,
+/// 2 + 3 = 5,
/// ```
///
-/// The function passed to this macro must return `bool`. Each of the arguments
-/// must evaluate to a type implementing [`std::fmt::Debug`]. The debug output
-/// is used to construct the failure message.
-///
/// The predicate can also be a method on a struct, e.g.:
///
/// ```ignore
@@ -201,42 +204,45 @@
/// verify_pred!((AStruct {}).equals_modulo(a, b, n))?;
/// ```
///
-/// **Warning:** This macro assumes that the arguments passed to the predicate
-/// are either *variables* or *calls to pure functions*. If two subsequent
-/// invocations to any of the expresssions passed as arguments result in
-/// different values, then the output message of a test failure will deviate
-/// from the values actually passed to the predicate. For this reason, *always
-/// assign the outputs of non-pure functions to variables before using them in
-/// this macro. For example:
+/// The expression passed to this macro must return `bool`. In the most general
+/// case, it prints out each of the `.`-separated parts of the expression and
+/// the arguments of all top-level method calls as long as they implement
+/// `Debug`. It evaluates every value (including the method receivers) exactly
+/// once. Effectively, for `verify_pred!((a + 1).b.c(x + y, &mut z, 2))`, it
+/// generates code analogous to the following, which allows printing accurate
+/// intermediate values even if they are subsequently consumed (moved out) or
+/// mutated in-place by the expression:
///
/// ```ignore
-/// let output = generate_random_number(); // Assigned outside of verify_pred.
-/// verify_pred!(is_sufficiently_random(output))?;
+/// let mut error_message = "(a + 1).b.c(x + y, 2) was false with".to_string();
+/// let mut x1 = (a + 1);
+/// write!(error_message, "\n (a + 1) = {:?},", x1);
+/// write!(error_message, "\n (a + 1).b = {:?},", x1.b);
+/// let mut x2 = x + y;
+/// write!(error_message, "\n x + y = {:?},", x2);
+/// let mut x3 = &mut z;
+/// write!(error_message, "\n & mut z = {:?},", x3);
+/// let mut x4 = x1.b.c(x2, x3, 2);
+/// if (x4) {
+/// Ok(())
+/// } else {
+/// Err(error_message)
+/// }
/// ```
+///
+/// Wrapping the passed-in expression in parens or curly braces will prevent the
+/// detailed printing of the expression.
+///
+/// ```ignore
+/// verify_pred!((a.foo()).bar())?;
+/// ```
+///
+/// would not print `a`, but would print `(a.foo())` and `(a.foo()).bar()` on
+/// error.
#[macro_export]
macro_rules! verify_pred {
- ([$($predicate:tt)*]($($arg:tt),* $(,)?)) => {
- if !$($predicate)*($($arg),*) {
- $crate::assertions::internal::report_failed_predicate(
- concat!(stringify!($($predicate)*), stringify!(($($arg),*))),
- vec![$((format!(concat!(stringify!($arg), " = {:?}"), $arg))),*],
- $crate::internal::source_location::SourceLocation::new(
- file!(),
- line!(),
- column!(),
- ),
- )
- } else {
- Ok(())
- }
- };
-
- ([$($predicate:tt)*] $first:tt $($rest:tt)*) => {
- $crate::verify_pred!([$($predicate)* $first] $($rest)*)
- };
-
- ($first:tt $($rest:tt)*) => {
- $crate::verify_pred!([$first] $($rest)*)
+ ($expr:expr $(,)?) => {
+ $crate::assertions::internal::__googletest_macro_verify_pred!($expr)
};
}
@@ -285,27 +291,978 @@
/// [`and_log_failure`](crate::GoogleTestSupport::and_log_failure).
#[macro_export]
macro_rules! fail {
- ($($message:expr),+) => {{
- // We wrap this in a function so that we can annotate it with the must_use attribute.
- // must_use on expressions is still experimental.
- #[must_use = "The assertion result must be evaluated to affect the test result."]
- fn create_fail_result(message: String) -> $crate::Result<()> {
- Err($crate::internal::test_outcome::TestAssertionFailure::create(format!(
- "{}\n{}",
- message,
- $crate::internal::source_location::SourceLocation::new(
- file!(),
- line!(),
- column!(),
- ),
- )))
- }
- create_fail_result(format!($($message),*))
+ ($($message:expr),+ $(,)?) => {{
+ $crate::assertions::internal::create_fail_result(
+ format!($($message),*),
+ )
}};
() => { fail!("Test failed") };
}
+/// Generates a success. This **does not** make the overall test succeed. A test
+/// is only considered successful if none of its assertions fail during its
+/// execution.
+///
+/// The succeed!() assertion is purely documentary. The only user visible output
+/// is a stdout with information on where the success was generated from.
+///
+/// ```ignore
+/// fn test_to_be_implemented() {
+/// succeed!();
+/// }
+/// ```
+///
+/// One may include formatted arguments in the success message:
+///
+/// ```ignore
+/// fn test_to_be_implemented() {
+/// succeed!("I am just a fake test: {}", "a fake test indeed");
+/// }
+/// ```
+#[macro_export]
+macro_rules! succeed {
+ ($($message:expr),+ $(,)?) => {{
+ println!(
+ "{}\n at {}:{}:{}",
+ format!($($message),*),
+ file!(), line!(), column!()
+ );
+ }};
+
+ () => {
+ succeed!("Success")
+ };
+}
+
+/// Generates a failure marking the test as failed but continue execution.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail_but_not_abort() {
+/// add_failure!();
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail_but_not_abort() {
+/// add_failure!("I am just a fake test: {}", "a fake test indeed");
+/// }
+/// ```
+#[macro_export]
+macro_rules! add_failure {
+ ($($message:expr),+ $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ $crate::assertions::internal::create_fail_result(
+ format!($($message),*),
+ ).and_log_failure();
+ }};
+
+ () => {
+ add_failure!("Failed")
+ };
+}
+
+/// Generates a failure at specified location marking the test as failed but
+/// continue execution.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail_but_not_abort() {
+/// add_failure_at!("src/my_file.rs", 32, 12);
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail_but_not_abort() {
+/// add_failure_at!(
+/// "src/my_file.rs",
+/// 32,
+/// 12,
+/// "I am just a fake test: {}", "a fake test indeed",
+/// );
+/// }
+/// ```
+#[macro_export]
+macro_rules! add_failure_at {
+ ($file:expr, $line:expr, $column:expr, $($message:expr),+ $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ $crate::assertions::internal::create_fail_result(
+ format!($($message),*),
+ ).map_err(|e| e.with_fake_location($file, $line, $column)).and_log_failure();
+ }};
+
+ ($file:expr, $line:expr, $column:expr $(,)?) => {
+ add_failure_at!($file, $line, $column, "Failed")
+ };
+}
+
+/// Verify if the condition evaluates to true and returns `Result`.
+///
+/// Evaluates to `Result::Ok(())` if the condition is true and
+/// `Result::Err(TestAssertionFailure)` if it evaluates to false. The caller
+/// must then decide how to handle the `Err` variant. It has a few options:
+/// * Abort the current function with the `?` operator. This requires that the
+/// function return a suitable `Result`.
+/// * Log the failure and continue by calling the method `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+/// verify_true!(2 + 2 == 5)
+/// }
+/// ```
+#[macro_export]
+macro_rules! verify_true {
+ ($condition:expr) => {{
+ use $crate::assertions::internal::Subject as _;
+ ($condition).check($crate::matchers::eq(true), stringify!($condition))
+ }};
+}
+
+/// Marks test as failed and continue execution if the expression evaluates to
+/// false.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// expect_true!(2 + 2 == 5);
+/// println!("This will print");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_true {
+ ($condition:expr) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_true!($condition).and_log_failure()
+ }};
+}
+
+/// Verify if the condition evaluates to false and returns `Result`.
+///
+/// Evaluates to `Result::Ok(())` if the condition is false and
+/// `Result::Err(TestAssertionFailure)` if it evaluates to true. The caller
+/// must then decide how to handle the `Err` variant. It has a few options:
+/// * Abort the current function with the `?` operator. This requires that the
+/// function return a suitable `Result`.
+/// * Log the failure and continue by calling the method `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+/// verify_false!(2 + 2 == 4)
+/// }
+/// ```
+#[macro_export]
+macro_rules! verify_false {
+ ($condition:expr) => {{
+ use $crate::assertions::internal::Subject as _;
+ ($condition).check($crate::matchers::eq(false), stringify!($condition))
+ }};
+}
+
+/// Marks test as failed and continue execution if the expression evaluates to
+/// true.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// expect_false!(2 + 2 == 4);
+/// println!("This will print");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_false {
+ ($condition:expr) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_false!($condition).and_log_failure()
+ }};
+}
+
+/// Checks whether the second argument is equal to the first argument.
+///
+/// Evaluates to `Result::Ok(())` if they are equal and
+/// `Result::Err(TestAssertionFailure)` if they are not. The caller must then
+/// decide how to handle the `Err` variant. It has a few options:
+/// * Abort the current function with the `?` operator. This requires that the
+/// function return a suitable `Result`.
+/// * Log the test failure and continue by calling the method
+/// `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+/// verify_eq!(2, 1)
+/// }
+/// ```
+///
+/// This macro has special support for matching against container. Namely:
+/// * `verify_eq!(actual, [e1, e2, ...])` is equivalent to
+/// `verify_that!(actual, elements_are![eq(e1), eq(e2), ...])`
+/// * `verify_eq!(actual, {e1, e2, ...})` is equivalent to
+/// `verify_that!(actual, unordered_elements_are![eq(e1), eq(e2), ...])`
+#[macro_export]
+macro_rules! verify_eq {
+ // Specialization for ordered sequences of tuples:
+ ($actual:expr, [ $( ( $($tuple_elt:expr),* ) ),+ $(,)? ] $(,)?) => {
+ verify_that!(&$actual, [
+ $(
+ // tuple matching
+ (
+ $(
+ $crate::matchers::eq(&$tuple_elt)
+ ),*
+ )
+ ),*
+ ])
+ };
+
+ // Specialization for unordered sequences of tuples:
+ ($actual:expr, { $( ( $($tuple_elt:expr),* ) ),+ $(,)?} $(,)?) => {
+ verify_that!(&$actual, {
+ $(
+ // tuple matching
+ (
+ $(
+ $crate::matchers::eq(&$tuple_elt)
+ ),*
+ )
+ ),*
+ })
+ };
+
+ // Ordered sequences:
+ ($actual:expr, [$($expected:expr),+ $(,)?] $(,)?) => {
+ verify_that!(&$actual, [$($crate::matchers::eq(&$expected)),*])
+ };
+
+ // Unordered sequences:
+ ($actual:expr, {$($expected:expr),+ $(,)?} $(,)?) => {
+ verify_that!(&$actual, {$($crate::matchers::eq(&$expected)),*})
+ };
+
+ // General case:
+ ($actual:expr, $expected:expr $(,)?) => {
+ verify_that!(&$actual, $crate::matchers::eq(&$expected))
+ };
+}
+
+/// Marks test as failed and continues execution if the second argument is not
+/// equal to first argument.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// expect_eq!(2, 1);
+/// println!("This will print!");
+/// }
+/// ```
+///
+/// This macro has special support for matching against container. Namely:
+/// * `expect_eq!(actual, [e1, e2, ...])` for checking actual contains "e1, e2,
+/// ..." in order.
+/// * `expect_eq!(actual, {e1, e2, ...})` for checking actual contains "e1, e2,
+/// ..." in any order.
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// let argument = "argument"
+/// expect_eq!(2, 1, "custom failure message: {argument}");
+/// println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_eq {
+ ($actual:expr, [$($expected:expr),+ $(,)?] $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_eq!($actual, [$($expected),*]).and_log_failure();
+ }};
+ ($actual:expr, [$($expected:expr),+ $(,)?], $($format_args:expr),* $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_eq!($actual, [$($expected),*])
+ .with_failure_message(|| format!($($format_args),*))
+ .and_log_failure();
+ }};
+ ($actual:expr, {$($expected:expr),+ $(,)?} $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_eq!($actual, {$($expected),*}).and_log_failure();
+ }};
+ ($actual:expr, {$($expected:expr),+ $(,)?}, $($format_args:expr),* $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_eq!($actual, {$($expected),*})
+ .with_failure_message(|| format!($($format_args),*))
+ .and_log_failure();
+ }};
+ ($actual:expr, $expected:expr $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_eq!($actual, $expected).and_log_failure();
+ }};
+ ($actual:expr, $expected:expr, $($format_args:expr),* $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_eq!($actual, $expected)
+ .with_failure_message(|| format!($($format_args),*))
+ .and_log_failure();
+ }};
+}
+
+/// Checks whether the second argument is not equal to the first argument.
+///
+/// Evaluates to `Result::Ok(())` if they are not equal and
+/// `Result::Err(TestAssertionFailure)` if they are equal. The caller must then
+/// decide how to handle the `Err` variant. It has a few options:
+/// * Abort the current function with the `?` operator. This requires that the
+/// function return a suitable `Result`.
+/// * Log the test failure and continue by calling the method
+/// `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+/// verify_ne!(1, 1)
+/// }
+/// ```
+#[macro_export]
+macro_rules! verify_ne {
+ ($actual:expr, $expected:expr $(,)?) => {
+ verify_that!(&$actual, $crate::matchers::not($crate::matchers::eq(&$expected)))
+ };
+}
+
+/// Marks test as failed and continues execution if the second argument is
+/// equal to first argument.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// expect_ne!(1, 1);
+/// println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// let argument = "argument"
+/// expect_ne!(1, 1, "custom failure message: {argument}");
+/// println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_ne {
+ ($actual:expr, $expected:expr, $($format_args:expr),+ $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_ne!($actual, $expected)
+ .with_failure_message(|| format!($($format_args),*))
+ .and_log_failure();
+ }};
+ ($actual:expr, $expected:expr $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_ne!($actual, $expected).and_log_failure();
+ }};
+}
+
+/// Checks whether the first argument is less than second argument.
+///
+/// Evaluates to `Result::Ok(())` if the first argument is less than the second
+/// and `Result::Err(TestAssertionFailure)` if it is greater or equal. The
+/// caller must then decide how to handle the `Err` variant. It has a few
+/// options:
+/// * Abort the current function with the `?` operator. This requires that the
+/// function return a suitable `Result`.
+/// * Log the test failure and continue by calling the method
+/// `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+/// verify_lt!(2, 1)
+/// }
+#[macro_export]
+macro_rules! verify_lt {
+ ($actual:expr, $expected:expr $(,)?) => {
+ verify_that!($actual, $crate::matchers::lt($expected))
+ };
+}
+
+/// Marks test as failed and continues execution if the first argument is
+/// greater or equal to second argument.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// expect_lt!(2, 1);
+/// println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// let argument = "argument"
+/// expect_lt!(1, 1, "custom failure message: {argument}");
+/// println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_lt {
+ ($actual:expr, $expected:expr, $($format_args:expr),+ $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_lt!($actual, $expected)
+ .with_failure_message(|| format!($($format_args),*))
+ .and_log_failure();
+ }};
+ ($actual:expr, $expected:expr $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_lt!($actual, $expected).and_log_failure();
+ }};
+}
+
+/// Checks whether the first argument is less than or equal to the second
+/// argument.
+///
+/// Evaluates to `Result::Ok(())` if the first argument is less than or equal to
+/// the second and `Result::Err(TestAssertionFailure)` if it is greater. The
+/// caller must then decide how to handle the `Err` variant. It has a few
+/// options:
+/// * Abort the current function with the `?` operator. This requires that the
+/// function return a suitable `Result`.
+/// * Log the test failure and continue by calling the method
+/// `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+/// verify_le!(2, 1)
+/// }
+#[macro_export]
+macro_rules! verify_le {
+ ($actual:expr, $expected:expr $(,)?) => {
+ verify_that!($actual, $crate::matchers::le($expected))
+ };
+}
+
+/// Marks test as failed and continues execution if the first argument is
+/// greater than the second argument.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// expect_le!(2, 1);
+/// println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// let argument = "argument"
+/// expect_le!(2, 1, "custom failure message: {argument}");
+/// println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_le {
+ ($actual:expr, $expected:expr, $($format_args:expr),+ $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_le!($actual, $expected)
+ .with_failure_message(|| format!($($format_args),*))
+ .and_log_failure();
+ }};
+ ($actual:expr, $expected:expr $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_le!($actual, $expected).and_log_failure();
+ }};
+}
+
+/// Checks whether the first argument is greater than the second argument.
+///
+/// Evaluates to `Result::Ok(())` if the first argument is greater than
+/// the second and `Result::Err(TestAssertionFailure)` if it is not. The
+/// caller must then decide how to handle the `Err` variant. It has a few
+/// options:
+/// * Abort the current function with the `?` operator. This requires that the
+/// function return a suitable `Result`.
+/// * Log the test failure and continue by calling the method
+/// `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+/// verify_gt!(1, 2)
+/// }
+#[macro_export]
+macro_rules! verify_gt {
+ ($actual:expr, $expected:expr $(,)?) => {
+ verify_that!($actual, $crate::matchers::gt($expected))
+ };
+}
+
+/// Marks test as failed and continues execution if the first argument is
+/// not greater than the second argument.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// expect_gt!(1, 2);
+/// println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// let argument = "argument"
+/// expect_gt!(1, 2, "custom failure message: {argument}");
+/// println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_gt {
+ ($actual:expr, $expected:expr, $($format_args:expr),+ $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_gt!($actual, $expected)
+ .with_failure_message(|| format!($($format_args),*))
+ .and_log_failure();
+ }};
+ ($actual:expr, $expected:expr $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_gt!($actual, $expected).and_log_failure();
+ }};
+}
+
+/// Checks whether the first argument is greater than or equal to the second
+/// argument.
+///
+/// Evaluates to `Result::Ok(())` if the first argument is greater than or equal
+/// to the second and `Result::Err(TestAssertionFailure)` if it is not. The
+/// caller must then decide how to handle the `Err` variant. It has a few
+/// options:
+/// * Abort the current function with the `?` operator. This requires that the
+/// function return a suitable `Result`.
+/// * Log the test failure and continue by calling the method
+/// `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+/// verify_ge!(1, 2)
+/// }
+/// ```
+#[macro_export]
+macro_rules! verify_ge {
+ ($actual:expr, $expected:expr $(,)?) => {
+ verify_that!($actual, $crate::matchers::ge($expected))
+ };
+}
+
+/// Marks test as failed and continues execution if the first argument is
+/// not greater than or equal to the second argument.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// expect_ge!(1, 2);
+/// println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// let argument = "argument"
+/// expect_ge!(1, 2, "custom failure message: {argument}");
+/// println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_ge {
+ ($actual:expr, $expected:expr, $($format_args:expr),+ $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_ge!($actual, $expected)
+ .with_failure_message(|| format!($($format_args),*))
+ .and_log_failure();
+ }};
+ ($actual:expr, $expected:expr $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_ge!($actual, $expected).and_log_failure();
+ }};
+}
+
+/// Checks whether the float given by first argument is approximately
+/// equal to second argument.
+///
+/// This automatically computes a tolerance from the magnitude of `expected` and
+/// matches any actual value within this tolerance of the expected value. The
+/// tolerance is chosen to account for the inaccuracies in most ordinary
+/// floating point calculations. To see details of how the tolerance is
+/// calculated look at the implementation of
+/// [`googletest::approx_eq`][crate::matchers::approx_eq].
+///
+/// Evaluates to `Result::Ok(())` if the first argument is approximately equal
+/// to the second and `Result::Err(TestAssertionFailure)` if it is not. The
+/// caller must then decide how to handle the `Err` variant. It has a few
+/// options:
+/// * Abort the current function with the `?` operator. This requires that the
+/// function return a suitable `Result`.
+/// * Log the test failure and continue by calling the method
+/// `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+/// verify_float_eq!(1.0, 2.0)
+/// }
+/// ```
+#[macro_export]
+macro_rules! verify_float_eq {
+ ($actual:expr, $expected:expr $(,)?) => {
+ verify_that!($actual, $crate::matchers::approx_eq($expected))
+ };
+}
+
+/// Marks test as failed and continues execution if the float given by the first
+/// argument is not approximately equal to the float given by the second
+/// argument.
+///
+/// This automatically computes a tolerance from the magnitude of `expected` and
+/// matches any actual value within this tolerance of the expected value. The
+/// tolerance is chosen to account for the inaccuracies in most ordinary
+/// floating point calculations. To see details of how the tolerance is
+/// calculated look at the implementation of
+/// [`googletest::approx_eq`][crate::matchers::approx_eq].
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// expect_float_eq!(1.0, 2.0);
+/// println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// let argument = "argument"
+/// expect_float_eq!(1.0, 2.0, "custom failure message: {argument}");
+/// println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_float_eq {
+ ($actual:expr, $expected:expr, $($format_args:expr),+ $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_float_eq!($actual, $expected)
+ .with_failure_message(|| format!($($format_args),*))
+ .and_log_failure();
+ }};
+ ($actual:expr, $expected:expr $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_float_eq!($actual, $expected).and_log_failure();
+ }};
+}
+
+/// Checks whether the float given by first argument is equal to second argument
+/// with error tolerance of max_abs_error.
+///
+/// Evaluates to `Result::Ok(())` if the first argument is approximately equal
+/// to the second and `Result::Err(TestAssertionFailure)` if it is not. The
+/// caller must then decide how to handle the `Err` variant. It has a few
+/// options:
+/// * Abort the current function with the `?` operator. This requires that the
+/// function return a suitable `Result`.
+/// * Log the test failure and continue by calling the method
+/// `and_log_failure`.
+///
+/// Of course, one can also use all other standard methods on `Result`.
+///
+/// **Invoking this macro by itself does not cause a test failure to be recorded
+/// or output.** The resulting `Result` must be handled as described above to
+/// cause the test to be recorded as a failure.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[test]
+/// fn should_fail() -> Result<()> {
+/// verify_near!(1.12345, 1.12346, 1e-6)
+/// }
+/// ```
+#[macro_export]
+macro_rules! verify_near {
+ ($actual:expr, $expected:expr, $max_abs_error:expr $(,)?) => {
+ verify_that!($actual, $crate::matchers::near($expected, $max_abs_error))
+ };
+}
+
+/// Marks the test as failed and continues execution if the float given by first
+/// argument is not equal to second argument with error tolerance of
+/// max_abs_error.
+///
+/// This is a **not-fatal** failure. The test continues execution even after the
+/// macro execution.
+///
+/// This can only be invoked inside tests with the
+/// [`gtest`][crate::gtest] attribute. The failure must be generated
+/// in the same thread as that running the test itself.
+///
+/// Example:
+/// ```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// expect_near!(1.12345, 1.12346, 1e-6);
+/// println!("This will print!");
+/// }
+/// ```
+///
+/// One may include formatted arguments in the failure message:
+///```ignore
+/// use googletest::prelude::*;
+///
+/// #[gtest]
+/// fn should_fail() {
+/// let argument = "argument"
+/// expect_near!(1.12345, 1.12346, 1e-6, "custom failure message: {argument}");
+/// println!("This will print!");
+/// }
+/// ```
+#[macro_export]
+macro_rules! expect_near {
+ ($actual:expr, $expected:expr, $max_abs_error:expr, $($format_args:expr),+ $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_near!($actual, $expected, $max_abs_error)
+ .with_failure_message(|| format!($($format_args),*))
+ .and_log_failure();
+ }};
+ ($actual:expr, $expected:expr, $max_abs_error:expr $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ verify_near!($actual, $expected, $max_abs_error).and_log_failure();
+ }};
+}
+
/// Matches the given value against the given matcher, panicking if it does not
/// match.
///
@@ -351,6 +1308,59 @@
/// equivalent to `ASSERT_THAT`, use [`verify_that!`] with the `?` operator.
#[macro_export]
macro_rules! assert_that {
+ // specialized to sequence:
+ ($actual:expr, [ $($expected:expr),* ] $(,)?) => {
+ match $crate::verify_that!($actual, [ $($expected),* ]) {
+ Ok(_) => {}
+ Err(e) => {
+ // The extra newline before the assertion failure message makes the failure a
+ // bit easier to read when there's some generic boilerplate from the panic.
+ panic!("\n{}", e);
+ }
+ }
+ };
+
+ // specialized to unordered sequence
+ ($actual:expr, { $($expected:expr),* } $(,)?) => {
+ match $crate::verify_that!($actual, { $($expected),* }) {
+ Ok(_) => {}
+ Err(e) => {
+ // The extra newline before the assertion failure message makes the failure a
+ // bit easier to read when there's some generic boilerplate from the panic.
+ panic!("\n{}", e);
+ }
+ }
+ };
+
+ // w/ format args, specialized to sequence:
+ ($actual:expr, [ $($expected:expr),* ], $($format_args:expr),* $(,)?) => {
+ match $crate::verify_that!($actual, [ $($expected),* ])
+ .with_failure_message(|| format!($($format_args),*))
+ {
+ Ok(_) => {}
+ Err(e) => {
+ // The extra newline before the assertion failure message makes the failure a
+ // bit easier to read when there's some generic boilerplate from the panic.
+ panic!("\n{}", e);
+ }
+ }
+ };
+
+ // w/ format args, specialized to unordered sequence:
+ ($actual:expr, { $($expected:expr),* }, $($format_args:expr),* $(,)?) => {
+ match $crate::verify_that!($actual, { $($expected),* })
+ .with_failure_message(|| format!($($format_args),*))
+ {
+ Ok(_) => {}
+ Err(e) => {
+ // The extra newline before the assertion failure message makes the failure a
+ // bit easier to read when there's some generic boilerplate from the panic.
+ panic!("\n{}", e);
+ }
+ }
+ };
+
+ // general case:
($actual:expr, $expected:expr $(,)?) => {
match $crate::verify_that!($actual, $expected) {
Ok(_) => {}
@@ -362,6 +1372,7 @@
}
};
+ // w/ format args, general case:
($actual:expr, $expected:expr, $($format_args:expr),* $(,)?) => {
match $crate::verify_that!($actual, $expected)
.with_failure_message(|| format!($($format_args),*))
@@ -405,7 +1416,7 @@
/// execution in the event of assertion failure.
///
/// This can only be invoked inside tests with the
-/// [`googletest::test`][crate::test] attribute. The assertion must
+/// [`gtest`][crate::gtest] attribute. The assertion must
/// occur in the same thread as that running the test itself.
///
/// Invoking this macro is equivalent to using
@@ -442,12 +1453,43 @@
/// ```
#[macro_export]
macro_rules! expect_that {
+ // specialized to sequence:
+ ($actual:expr, [$($expected:expr),*] $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ $crate::verify_that!($actual, [$($expected),*]).and_log_failure();
+ }};
+
+ // specialized to unordered sequence:
+ ($actual:expr, {$($expected:expr),*} $(,)?) => {{
+ use $crate::GoogleTestSupport as _;
+ $crate::verify_that!($actual, {$($expected),*}).and_log_failure();
+ }};
+
+ // w/ format args, specialized to sequence:
+ ($actual:expr, [$($expected:expr),*], $($format_args:expr),* $(,)?) => {
+ use $crate::GoogleTestSupport as _;
+ $crate::verify_that!($actual, [$($expected),*])
+ .with_failure_message(|| format!($($format_args),*))
+ .and_log_failure()
+ };
+
+ // w/ format args, specialized to unordered sequence:
+ ($actual:expr, {$($expected:expr),*}, $($format_args:expr),* $(,)?) => {
+ use $crate::GoogleTestSupport as _;
+ $crate::verify_that!($actual, {$($expected),*})
+ .with_failure_message(|| format!($($format_args),*))
+ .and_log_failure()
+ };
+
+ // general case:
($actual:expr, $expected:expr $(,)?) => {{
- use $crate::GoogleTestSupport;
+ use $crate::GoogleTestSupport as _;
$crate::verify_that!($actual, $expected).and_log_failure();
}};
+ // w/ format args, general case:
($actual:expr, $expected:expr, $($format_args:expr),* $(,)?) => {
+ use $crate::GoogleTestSupport as _;
$crate::verify_that!($actual, $expected)
.with_failure_message(|| format!($($format_args),*))
.and_log_failure()
@@ -461,7 +1503,7 @@
/// continues execution in the event of assertion failure.
///
/// This can only be invoked inside tests with the
-/// [`googletest::test`][crate::test] attribute. The assertion must
+/// [`gtest`][crate::gtest] attribute. The assertion must
/// occur in the same thread as that running the test itself.
///
/// Invoking this macro is equivalent to using
@@ -473,7 +1515,7 @@
#[macro_export]
macro_rules! expect_pred {
($($content:tt)*) => {{
- use $crate::GoogleTestSupport;
+ use $crate::GoogleTestSupport as _;
$crate::verify_pred!($($content)*).and_log_failure();
}};
}
@@ -484,50 +1526,142 @@
#[doc(hidden)]
pub mod internal {
use crate::{
- internal::{source_location::SourceLocation, test_outcome::TestAssertionFailure},
+ internal::test_outcome::TestAssertionFailure,
matcher::{create_assertion_failure, Matcher, MatcherResult},
};
use std::fmt::Debug;
- /// Checks whether the matcher `expected` matches the value `actual`, adding
- /// a test failure report if it does not match.
- ///
- /// Returns `Ok(())` if the value matches and `Err(())` if it does not
- /// match.
- ///
- /// **For internal use only. API stablility is not guaranteed!**
- #[must_use = "The assertion result must be evaluated to affect the test result."]
- pub fn check_matcher<T: Debug + ?Sized>(
- actual: &T,
- expected: impl Matcher<ActualT = T>,
- actual_expr: &'static str,
- source_location: SourceLocation,
- ) -> Result<(), TestAssertionFailure> {
- match expected.matches(actual) {
- MatcherResult::Match => Ok(()),
- MatcherResult::NoMatch => {
- Err(create_assertion_failure(&expected, actual, actual_expr, source_location))
+ pub use ::googletest_macro::__googletest_macro_verify_pred;
+
+ /// Extension trait to perform autoref through method lookup in the
+ /// assertion macros. With this trait, the subject can be either a value
+ /// or a reference. For example, this trait makes the following code
+ /// compile and work:
+ /// ```
+ /// # use googletest::prelude::*;
+ /// # fn would_not_compile_without_autoref() -> Result<()> {
+ /// let not_copyable = vec![1,2,3];
+ /// verify_that!(not_copyable, empty())?;
+ /// # Ok(())
+ /// # }
+ /// ```
+ /// See [Method Lookup](https://rustc-dev-guide.rust-lang.org/method-lookup.html)
+ pub trait Subject: Copy + Debug {
+ /// Checks whether the matcher `expected` matches the `Subject `self`,
+ /// adding a test failure report if it does not match.
+ ///
+ /// Returns `Ok(())` if the value matches and `Err(_)` if it does not
+ /// match.
+ ///
+ /// **For internal use only. API stablility is not guaranteed!**
+ #[must_use = "The assertion result must be evaluated to affect the test result."]
+ #[track_caller]
+ fn check(
+ self,
+ expected: impl Matcher<Self>,
+ actual_expr: &'static str,
+ ) -> Result<(), TestAssertionFailure> {
+ match expected.matches(self) {
+ MatcherResult::Match => Ok(()),
+ MatcherResult::NoMatch => {
+ Err(create_assertion_failure(&expected, self, actual_expr))
+ }
}
}
}
- /// Constructs a `Result::Err(TestAssertionFailure)` for a predicate failure
- /// as produced by the macro [`crate::verify_pred`].
+ impl<T: Copy + Debug> Subject for T {}
+
+ /// Creates a failure at specified location.
///
- /// This intended only for use by the macro [`crate::verify_pred`].
- ///
- /// **For internal use only. API stablility is not guaranteed!**
+ /// **For internal use only. API stability is not guaranteed!**
#[must_use = "The assertion result must be evaluated to affect the test result."]
- pub fn report_failed_predicate(
- actual_expr: &'static str,
- formatted_arguments: Vec<String>,
- source_location: SourceLocation,
- ) -> Result<(), TestAssertionFailure> {
- Err(TestAssertionFailure::create(format!(
- "{} was false with\n {}\n{}",
- actual_expr,
- formatted_arguments.join(",\n "),
- source_location,
- )))
+ #[track_caller]
+ pub fn create_fail_result(message: String) -> crate::Result<()> {
+ Err(crate::internal::test_outcome::TestAssertionFailure::create(message))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::prelude::*;
+
+ #[test]
+ fn verify_of_hash_maps_with_str_string_matching() -> Result<()> {
+ let hash_map: std::collections::HashMap<String, String> =
+ std::collections::HashMap::from([("a".into(), "A".into()), ("b".into(), "B".into())]);
+ verify_eq!(hash_map, {("a", "A"), ("b", "B")})
+ }
+
+ #[test]
+ fn verify_of_hash_maps_with_ad_hoc_struct() -> Result<()> {
+ #[derive(PartialEq, Debug)]
+ struct Greek(String);
+
+ let hash_map: std::collections::HashMap<String, Greek> = std::collections::HashMap::from([
+ ("a".into(), Greek("Alpha".into())),
+ ("b".into(), Greek("Beta".into())),
+ ]);
+ verify_eq!(hash_map, {
+ ("b", Greek("Beta".into())),
+ ("a", Greek("Alpha".into())),
+ })
+ }
+
+ #[test]
+ fn verify_of_hash_maps_with_i32s() -> Result<()> {
+ let hash_map: std::collections::HashMap<i32, i32> =
+ std::collections::HashMap::from([(1, 1), (2, 4), (-1, 1), (-3, 9)]);
+ verify_eq!(hash_map, {
+ (-3, 9),
+ (-1, 1),
+ (1, 1),
+ (2, 4),
+ })
+ }
+
+ #[test]
+ fn verify_eq_of_unordered_pairs() -> Result<()> {
+ verify_eq!(vec![(1, 2), (2, 3)], {(1, 2), (2, 3)})?;
+ verify_eq!(vec![(1, 2), (2, 3)], {(2, 3), (1, 2)})
+ }
+
+ #[test]
+ fn verify_eq_of_unordered_structs() -> Result<()> {
+ #[derive(PartialEq, Debug)]
+ struct P(i32, i32);
+
+ verify_eq!(vec![P(1, 1), P(1, 2), P(3, 7)],
+ {P(1, 1), P(1, 2), P(3, 7)})?;
+ verify_eq!(vec![P(1, 1), P(1, 2), P(3, 7)],
+ {P(3,7), P(1, 1), P(1, 2)})
+ }
+
+ #[test]
+ fn verify_eq_of_ordered_pairs() -> Result<()> {
+ verify_eq!(vec![(1, 2), (2, 3)], [(1, 2), (2, 3)])
+ }
+
+ #[test]
+ fn verify_eq_of_ordered_structs() -> Result<()> {
+ #[derive(PartialEq, Debug)]
+ struct P(i32, i32);
+
+ verify_eq!(vec![P(1, 1), P(1, 2), P(3, 7)], [P(1, 1), P(1, 2), P(3, 7)])
+ }
+
+ #[test]
+ fn verify_eq_of_ordered_pairs_order_matters() -> Result<()> {
+ let result = verify_eq!(vec![(1, 2), (2, 3)], [(2, 3), (1, 2)]);
+ verify_that!(result, err(anything()))
+ }
+
+ #[test]
+ fn verify_eq_of_ordered_structs_order_matters() -> Result<()> {
+ #[derive(PartialEq, Debug)]
+ struct P(i32, i32);
+
+ let result = verify_eq!(vec![P(1, 1), P(1, 2), P(3, 7)], [P(3, 7), P(1, 1), P(1, 2)]);
+ verify_that!(result, err(anything()))
}
}
diff --git a/crates/googletest/src/description.rs b/crates/googletest/src/description.rs
index 9605559..e7339c1 100644
--- a/crates/googletest/src/description.rs
+++ b/crates/googletest/src/description.rs
@@ -89,6 +89,8 @@
pub struct Description {
elements: List,
initial_indentation: usize,
+ is_conjunction: bool,
+ is_disjunction: bool,
}
impl Description {
@@ -199,6 +201,27 @@
pub fn is_empty(&self) -> bool {
self.elements.is_empty()
}
+
+ pub(crate) fn push_in_last_nested(mut self, inner: Description) -> Self {
+ self.elements.push_at_end(inner.elements);
+ self
+ }
+
+ pub(crate) fn conjunction_description(self) -> Self {
+ Self { is_conjunction: true, ..self }
+ }
+
+ pub(crate) fn is_conjunction_description(&self) -> bool {
+ self.is_conjunction
+ }
+
+ pub(crate) fn disjunction_description(self) -> Self {
+ Self { is_disjunction: true, ..self }
+ }
+
+ pub(crate) fn is_disjunction_description(&self) -> bool {
+ self.is_disjunction
+ }
}
impl Display for Description {
@@ -359,4 +382,24 @@
)))
)
}
+
+ #[test]
+ fn new_is_empty() -> Result<()> {
+ verify_that!(Description::new(), predicate(Description::is_empty))
+ }
+
+ #[test]
+ fn text_is_not_empty() -> Result<()> {
+ verify_that!(Description::new().text("something"), not(predicate(Description::is_empty)))
+ }
+
+ #[test]
+ fn new_zero_length() -> Result<()> {
+ verify_that!(Description::new().len(), eq(0))
+ }
+
+ #[test]
+ fn text_one_length() -> Result<()> {
+ verify_that!(Description::new().text("something").len(), eq(1))
+ }
}
diff --git a/crates/googletest/src/fixtures.rs b/crates/googletest/src/fixtures.rs
new file mode 100644
index 0000000..27fc7c2
--- /dev/null
+++ b/crates/googletest/src/fixtures.rs
@@ -0,0 +1,261 @@
+// Copyright 2022 Google LLC
+//
+// 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.
+
+use std::{
+ any::{Any, TypeId},
+ collections::HashMap,
+ ops::{Deref, DerefMut},
+ sync::{Mutex, OnceLock},
+};
+
+/// Interface for structure to be set up and torn down as part of a test.
+/// Types implementing `Fixture` can be passed as a reference argument to a
+/// test function.
+///
+/// ```ignore
+/// strct MyFixture { ... }
+///
+/// impl Fixture for MyFixture { ... }
+///
+/// #[gtest]
+/// fn test_with_fixture(my_fixture: &MyFixture) {...}
+/// ```
+pub trait Fixture: Sized {
+ /// Factory method of the `Fixture`.
+ ///
+ /// This method is called by the test harness before the test case
+ /// If this method returns an `Err(...)`, then the test case is not
+ /// evaluated and only the fixtures previously set up are torn down.
+ fn set_up() -> crate::Result<Self>;
+
+ /// Clean up method for the fixture.
+ ///
+ /// This method is called by the test harness after the test case.
+ /// If the `Fixture` has been set up, the test harness will call this
+ /// method, even if the test case failed or panicked.
+ fn tear_down(self) -> crate::Result<()>;
+}
+
+/// Interface for structure to be set up before the test case.
+/// Types implementing `ConsumableFixture` can be passed by value to
+/// a test function.
+///
+/// ```ignore
+/// strct MyFixture { ... }
+///
+/// impl ConsumableFixture for MyFixture { ... }
+///
+/// #[gtest]
+/// fn test_with_fixture(my_fixture: MyFixture) {...}
+/// ```
+pub trait ConsumableFixture: Sized {
+ /// Factory method of the `ConsumableFixture`.
+ ///
+ /// This method is called by the test harness before the test case.
+ /// If this method returns an `Err(...)`, then the test case is not
+ /// evaluated.
+ fn set_up() -> crate::Result<Self>;
+}
+
+/// Generic adapter to implement `ConsumableFixture` on any type implementing
+/// `Default`.
+pub struct FixtureOf<T>(T);
+
+impl<T: Default> ConsumableFixture for FixtureOf<T> {
+ fn set_up() -> crate::Result<Self> {
+ Ok(Self(T::default()))
+ }
+}
+
+impl<T> Deref for FixtureOf<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl<T> DerefMut for FixtureOf<T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+/// Interface for structure to be set up only once before all tests.
+/// Types implementing `StaticFixture` can be passed as a double referenced
+/// argument to a test function.
+///
+/// ```ignore
+/// strct MyFixture{ ... }
+///
+/// impl StaticFixture for MyFixture { ... }
+///
+/// #[gtest]
+/// fn test_with_fixture(my_fixture: &&MyFixture){...}
+/// ```
+pub trait StaticFixture: Sized + Sync + Send {
+ /// Factory method of the `StaticFixture`.
+ ///
+ /// This method is called by the test harness before the first test case
+ /// using this fixture. If this method returns an `Err(...)`, then every
+ /// test cases using this fixture are not evaluated.
+ fn set_up_once() -> crate::Result<Self>;
+}
+
+impl<F: StaticFixture + 'static> Fixture for &'static F {
+ fn set_up() -> crate::Result<Self> {
+ static ONCE_FIXTURE_REPO: OnceLock<
+ Mutex<HashMap<TypeId, &'static (dyn Any + Sync + Send)>>,
+ > = OnceLock::new();
+ let mut map = ONCE_FIXTURE_REPO.get_or_init(|| Mutex::new(HashMap::new())).lock()?;
+ let any =
+ map.entry(TypeId::of::<F>()).or_insert_with(|| Box::leak(Box::new(F::set_up_once())));
+ match any.downcast_ref::<crate::Result<F>>() {
+ Some(Ok(ref fixture)) => Ok(fixture),
+ Some(Err(e)) => Err(e.clone()),
+ None => panic!("Downcast failed. This is a bug in GoogleTest Rust"),
+ }
+ }
+
+ // Note that this is `&F` being torn down, not `F`.
+ fn tear_down(self) -> crate::Result<()> {
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ use std::sync::Once;
+
+ use super::FixtureOf;
+ use super::StaticFixture;
+ use crate as googletest;
+ use crate::prelude::*;
+ use crate::test;
+
+ #[test]
+ fn fixture_no_fixture() -> Result<()> {
+ Ok(())
+ }
+
+ struct AlwaysSucceed;
+
+ impl Fixture for AlwaysSucceed {
+ fn set_up() -> crate::Result<Self> {
+ Ok(Self)
+ }
+
+ fn tear_down(self) -> crate::Result<()> {
+ Ok(())
+ }
+ }
+
+ #[test]
+ fn fixture_one_fixture(_: &AlwaysSucceed) -> Result<()> {
+ Ok(())
+ }
+
+ #[test]
+ fn fixture_three_fixtures(
+ _: &AlwaysSucceed,
+ _: &AlwaysSucceed,
+ _: &AlwaysSucceed,
+ ) -> Result<()> {
+ Ok(())
+ }
+
+ struct NotAFixture {
+ a_field: i32,
+ }
+
+ impl Default for NotAFixture {
+ fn default() -> Self {
+ Self { a_field: 33 }
+ }
+ }
+
+ #[test]
+ fn fixture_of_non_fixture(not_a_fixture: FixtureOf<NotAFixture>) -> Result<()> {
+ verify_that!(not_a_fixture.a_field, eq(33))
+ }
+
+ #[test]
+ fn fixture_of_non_fixture_mut(mut not_a_fixture: FixtureOf<NotAFixture>) -> Result<()> {
+ not_a_fixture.a_field += 10;
+ verify_that!(not_a_fixture.a_field, eq(43))
+ }
+ struct PanickyFixture;
+
+ impl Fixture for PanickyFixture {
+ fn set_up() -> crate::Result<Self> {
+ Ok(Self)
+ }
+
+ fn tear_down(self) -> crate::Result<()> {
+ panic!("Whoooops");
+ }
+ }
+
+ #[test]
+ #[should_panic(expected = "Whoooops")]
+ fn fixture_teardown_called_even_if_test_fail(_: &PanickyFixture) {
+ panic!("Test failed");
+ }
+
+ struct FailingTearDown;
+
+ impl Fixture for FailingTearDown {
+ fn set_up() -> crate::Result<Self> {
+ Ok(Self)
+ }
+
+ fn tear_down(self) -> crate::Result<()> {
+ Err(googletest::TestAssertionFailure::create("It must fail!".into()))
+ }
+ }
+
+ struct OnlyOnce;
+
+ impl StaticFixture for OnlyOnce {
+ fn set_up_once() -> crate::Result<Self> {
+ static ONCE: Once = Once::new();
+ assert!(!ONCE.is_completed());
+ ONCE.call_once(|| {});
+ Ok(Self)
+ }
+ }
+
+ #[test]
+ fn static_fixture_works(_: &&OnlyOnce) {}
+
+ #[test]
+ fn static_fixture_same_static_fixture_twice(once: &&OnlyOnce, twice: &&OnlyOnce) {
+ // checks it points to the same memory address.
+ let once: *const OnlyOnce = *once;
+ let twice: *const OnlyOnce = *twice;
+ expect_eq!(once, twice);
+ }
+
+ struct AnotherStaticFixture;
+
+ impl StaticFixture for AnotherStaticFixture {
+ fn set_up_once() -> crate::Result<Self> {
+ Ok(Self)
+ }
+ }
+
+ #[test]
+ fn static_fixture_two_different_static_fixtures(_: &&OnlyOnce, _: &&AnotherStaticFixture) {}
+}
diff --git a/crates/googletest/src/fmt.rs b/crates/googletest/src/fmt.rs
new file mode 100644
index 0000000..267b127
--- /dev/null
+++ b/crates/googletest/src/fmt.rs
@@ -0,0 +1,67 @@
+// Copyright 2024 Google LLC
+//
+// 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.
+
+/// Functions for use only by the procedural macros in this module.
+///
+/// **For internal use only. API stablility is not guaranteed!**
+#[doc(hidden)]
+pub mod internal {
+ use std::fmt::{Debug, Write};
+
+ /// Wrapper to allow for inherent-method specialization based on whether a
+ /// type implements `Debug`.
+ pub struct FormatWrapper<'a, T: ?Sized>(pub &'a T);
+
+ /// Default implementation to render values that implement `Debug`.
+ ///
+ /// Used for autoref specialization to conditionally
+ /// render only values that implement `Debug`. See also
+ /// [`FormatNonDebugFallback`].
+ impl<'a, T: Debug + ?Sized> FormatWrapper<'a, T> {
+ #[track_caller]
+ pub fn __googletest_write_expr_value(&self, output: &mut dyn Write, expr_label: &str) {
+ write!(output, "\n {} = {:?},", expr_label, self.0)
+ .expect("Formatting to String should never fail");
+ }
+ }
+
+ /// Fallback implementation for rendering values for non-`Debug` types..
+ ///
+ /// Used for inherent-method specialization to conditionally render only
+ /// values that implement `Debug`. See also the specialized inherent impl on
+ /// [`FormatWrapper`] above.
+ pub trait FormatNonDebugFallback {
+ fn __googletest_write_expr_value(&self, output: &mut dyn Write, expr_label: &str);
+ }
+
+ impl<'a, T: ?Sized> FormatNonDebugFallback for FormatWrapper<'a, T> {
+ #[track_caller]
+ fn __googletest_write_expr_value(&self, output: &mut dyn Write, expr_label: &str) {
+ write!(output, "\n {} does not implement Debug,", expr_label)
+ .expect("Formatting to String should never fail");
+ }
+ }
+
+ #[macro_export]
+ macro_rules! __googletest__write_expr_value(
+ ($output:expr, $expr_str:expr, $value:expr $(,)?) => {
+ {
+ use $crate::fmt::internal::FormatNonDebugFallback as _;
+ $crate::fmt::internal::FormatWrapper(&$value)
+ .__googletest_write_expr_value(&mut $output, $expr_str)
+ }
+ }
+ );
+ pub use __googletest__write_expr_value;
+}
diff --git a/crates/googletest/src/internal/description_renderer.rs b/crates/googletest/src/internal/description_renderer.rs
index 937f0d5..e84fabe 100644
--- a/crates/googletest/src/internal/description_renderer.rs
+++ b/crates/googletest/src/internal/description_renderer.rs
@@ -83,6 +83,17 @@
self.0.is_empty()
}
+ /// Append a new [`List`] in the last element which must be a
+ /// [`Block::Nested`]. Panic if `self` is empty or the last element is
+ /// not [`Block::Nested`].
+ pub(crate) fn push_at_end(&mut self, list: List) {
+ if let Some(Block::Nested(self_list)) = self.0.last_mut() {
+ self_list.push_nested(list);
+ } else {
+ panic!("pushing elements at the end of {self:#?} which last element is not Nested")
+ }
+ }
+
fn render_with_prefix(
&self,
f: &mut dyn Write,
diff --git a/crates/googletest/src/internal/mod.rs b/crates/googletest/src/internal/mod.rs
index 9bccdc3..1269bc9 100644
--- a/crates/googletest/src/internal/mod.rs
+++ b/crates/googletest/src/internal/mod.rs
@@ -15,5 +15,4 @@
#![doc(hidden)]
pub(crate) mod description_renderer;
-pub mod source_location;
pub mod test_outcome;
diff --git a/crates/googletest/src/internal/source_location.rs b/crates/googletest/src/internal/source_location.rs
deleted file mode 100644
index 4520c92..0000000
--- a/crates/googletest/src/internal/source_location.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// 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.
-
-use std::fmt::{Display, Error, Formatter};
-
-/// Encapsulates a location in source code.
-///
-/// This is intended to report the location of an assertion which failed to
-/// stdout.
-///
-/// **For internal use only. API stablility is not guaranteed!**
-#[doc(hidden)]
-pub struct SourceLocation {
- file: &'static str,
- line: u32,
- column: u32,
-}
-
-impl SourceLocation {
- /// Constructs a new [`SourceLocation`].
- ///
- /// **For internal use only. API stablility is not guaranteed!**
- #[doc(hidden)]
- pub fn new(file: &'static str, line: u32, column: u32) -> Self {
- Self { file, line, column }
- }
-}
-
-impl Display for SourceLocation {
- fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
- write!(f, " at {}:{}:{}", self.file, self.line, self.column)
- }
-}
diff --git a/crates/googletest/src/internal/test_outcome.rs b/crates/googletest/src/internal/test_outcome.rs
index 132ba99..edc8d2b 100644
--- a/crates/googletest/src/internal/test_outcome.rs
+++ b/crates/googletest/src/internal/test_outcome.rs
@@ -32,14 +32,13 @@
}
thread_local! {
- static CURRENT_TEST_OUTCOME: RefCell<Option<TestOutcome>> = RefCell::new(None);
+ static CURRENT_TEST_OUTCOME: RefCell<Option<TestOutcome>> = const { RefCell::new(None) };
}
impl TestOutcome {
/// Resets the current test's [`TestOutcome`].
///
- /// This is intended only for use by the attribute macro
- /// `#[googletest::test]`.
+ /// This is intended only for use by the attribute macro `#[gtest]`.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
@@ -86,6 +85,7 @@
/// Returns a `Result` corresponding to the outcome of the currently running
/// test.
+ #[track_caller]
pub(crate) fn get_current_test_outcome() -> Result<(), TestAssertionFailure> {
TestOutcome::with_current_test_outcome(|mut outcome| {
let outcome = outcome
@@ -117,12 +117,12 @@
}
/// Ensure that there is a test context present and panic if there is not.
- pub(crate) fn ensure_text_context_present() {
+ pub(crate) fn ensure_test_context_present() {
TestOutcome::with_current_test_outcome(|outcome| {
outcome.as_ref().expect(
"
No test context found.
- * Did you annotate the test with googletest::test?
+ * Did you annotate the test with gtest?
* Is the assertion running in the original test thread?
",
);
@@ -162,14 +162,50 @@
/// A human-readable formatted string describing the error.
pub description: String,
pub custom_message: Option<String>,
+ location: Location,
+}
+
+/// A code location.
+///
+/// `std::panic::Location` does not provide a constructor, hence we cannot
+/// construct a fake value.
+///
+/// **For internal use only. API stablility is not guaranteed!**
+#[doc(hidden)]
+#[derive(Clone)]
+enum Location {
+ Real(&'static std::panic::Location<'static>),
+ Fake { file: &'static str, line: u32, column: u32 },
+}
+
+impl Display for Location {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Location::Real(l) => write!(f, "{}", l),
+ Location::Fake { file, line, column } => write!(f, "{}:{}:{}", file, line, column),
+ }
+ }
}
impl TestAssertionFailure {
/// Creates a new instance with the given `description`.
///
/// **For internal use only. API stablility is not guaranteed!**
+ #[track_caller]
pub fn create(description: String) -> Self {
- Self { description, custom_message: None }
+ Self {
+ description,
+ custom_message: None,
+ location: Location::Real(std::panic::Location::caller()),
+ }
+ }
+
+ /// Set `location`` to a fake value.
+ ///
+ /// **For internal use only. API stablility is not guaranteed!**
+ pub fn with_fake_location(mut self, file: &'static str, line: u32, column: u32) -> Self {
+ self.location = Location::Fake { file, line, column };
+ self
}
pub(crate) fn log(&self) {
@@ -184,7 +220,7 @@
if let Some(custom_message) = &self.custom_message {
writeln!(f, "{}", custom_message)?;
}
- Ok(())
+ writeln!(f, " at {}", self.location)
}
}
@@ -198,6 +234,7 @@
}
impl<T: std::error::Error> From<T> for TestAssertionFailure {
+ #[track_caller]
fn from(value: T) -> Self {
TestAssertionFailure::create(format!("{value}"))
}
diff --git a/crates/googletest/src/lib.rs b/crates/googletest/src/lib.rs
index 51b6345..3f59546 100644
--- a/crates/googletest/src/lib.rs
+++ b/crates/googletest/src/lib.rs
@@ -22,11 +22,16 @@
#[macro_use]
pub mod assertions;
pub mod description;
+pub mod fixtures;
+#[macro_use]
+pub mod fmt;
pub mod internal;
pub mod matcher;
pub mod matcher_support;
pub mod matchers;
+pub use googletest_macro::{__abbreviated_stringify, __googletest_macro_verify_pred};
+
/// Re-exports of the symbols in this crate which are most likely to be used.
///
/// This includes:
@@ -42,16 +47,25 @@
/// }
/// ```
pub mod prelude {
- pub use super::matcher::Matcher;
+ pub use super::fixtures::{ConsumableFixture, Fixture, FixtureOf, StaticFixture};
+ pub use super::gtest;
+ pub use super::matcher::{Matcher, MatcherBase};
pub use super::matchers::*;
pub use super::verify_current_test_outcome;
pub use super::GoogleTestSupport;
pub use super::IntoTestResult;
pub use super::Result;
// Assert macros
- pub use super::{assert_that, expect_pred, expect_that, fail, verify_pred, verify_that};
+ pub use super::{
+ add_failure, add_failure_at, assert_pred, assert_that, expect_eq, expect_false,
+ expect_float_eq, expect_ge, expect_gt, expect_le, expect_lt, expect_ne, expect_near,
+ expect_pred, expect_that, expect_true, fail, succeed, verify_eq, verify_false,
+ verify_float_eq, verify_ge, verify_gt, verify_le, verify_lt, verify_ne, verify_near,
+ verify_pred, verify_that, verify_true,
+ };
}
+pub use googletest_macro::gtest;
pub use googletest_macro::test;
use internal::test_outcome::{TestAssertionFailure, TestOutcome};
@@ -89,13 +103,12 @@
/// `?` operator to continue execution of the test conditionally on there not
/// having been any failure yet.
///
-/// This requires the use of the [`#[googletest::test]`][crate::test] attribute
-/// macro.
+/// This requires the use of the [`#[gtest]`][crate::gtest] attribute macro.
///
/// ```
/// # use googletest::prelude::*;
/// # /* Make sure this also compiles as a doctest.
-/// #[googletest::test]
+/// #[gtest]
/// # */
/// # fn foo() -> u32 { 1 }
/// # fn bar() -> u32 { 2 }
@@ -108,6 +121,7 @@
/// }
/// # verify_that!(should_fail_and_not_execute_last_assertion(), err(displays_as(contains_substring("Test failed")))).unwrap();
/// ```
+#[track_caller]
pub fn verify_current_test_outcome() -> Result<()> {
TestOutcome::get_current_test_outcome()
}
@@ -203,7 +217,7 @@
impl<T> GoogleTestSupport for std::result::Result<T, TestAssertionFailure> {
fn and_log_failure(self) {
- TestOutcome::ensure_text_context_present();
+ TestOutcome::ensure_test_context_present();
if let Err(failure) = self {
failure.log();
}
@@ -229,44 +243,52 @@
///
/// A type can implement this trait to provide an easy way to return immediately
/// from a test in conjunction with the `?` operator. This is useful for
-/// [`Result`][std::result::Result] types whose `Result::Err` variant does not
-/// implement [`std::error::Error`].
+/// [`Option`] and [`Result`][std::result::Result] types whose `Result::Err`
+/// variant does not implement [`std::error::Error`].
///
-/// There is an implementation of this trait for [`anyhow::Error`] (which does
-/// not implement `std::error::Error`) when the `anyhow` feature is enabled.
-/// Importing this trait allows one to easily map [`anyhow::Error`] to a test
-/// failure:
+/// If `Result::Err` implements [`std::error::Error`] you can just use the `?`
+/// operator directly.
///
/// ```ignore
/// #[test]
-/// fn should_work() -> Result<()> {
+/// fn should_work() -> googletest::Result<()> {
/// let value = something_which_can_fail().into_test_result()?;
+/// let value = something_which_can_fail_with_option().into_test_result()?;
/// ...
/// }
///
-/// fn something_which_can_fail() -> anyhow::Result<...> { ... }
+/// fn something_which_can_fail() -> std::result::Result<T, String> { ... }
+/// fn something_which_can_fail_with_option() -> Option<T> { ... }
/// ```
pub trait IntoTestResult<T> {
/// Converts this instance into a [`Result`].
///
- /// Typically, the `Self` type is itself a [`std::result::Result`]. This
- /// method should then map the `Err` variant to a [`TestAssertionFailure`]
- /// and leave the `Ok` variant unchanged.
+ /// Typically, the `Self` type is itself an implementation of the
+ /// [`std::ops::Try`] trait. This method should then map the `Residual`
+ /// variant to a [`TestAssertionFailure`] and leave the `Output` variant
+ /// unchanged.
fn into_test_result(self) -> Result<T>;
}
-#[cfg(feature = "anyhow")]
-impl<T> IntoTestResult<T> for std::result::Result<T, anyhow::Error> {
+impl<T, E: std::fmt::Debug> IntoTestResult<T> for std::result::Result<T, E> {
+ #[track_caller]
fn into_test_result(self) -> std::result::Result<T, TestAssertionFailure> {
- self.map_err(|e| TestAssertionFailure::create(format!("{e}")))
+ match self {
+ Ok(t) => Ok(t),
+ Err(e) => Err(TestAssertionFailure::create(format!("{e:?}"))),
+ }
}
}
-#[cfg(feature = "proptest")]
-impl<OkT, CaseT: std::fmt::Debug> IntoTestResult<OkT>
- for std::result::Result<OkT, proptest::test_runner::TestError<CaseT>>
-{
- fn into_test_result(self) -> std::result::Result<OkT, TestAssertionFailure> {
- self.map_err(|e| TestAssertionFailure::create(format!("{e}")))
+impl<T> IntoTestResult<T> for Option<T> {
+ #[track_caller]
+ fn into_test_result(self) -> std::result::Result<T, TestAssertionFailure> {
+ match self {
+ Some(t) => Ok(t),
+ None => Err(TestAssertionFailure::create(format!(
+ "called `Option::into_test_result()` on a `Option::<{}>::None` value",
+ std::any::type_name::<T>()
+ ))),
+ }
}
}
diff --git a/crates/googletest/src/matcher.rs b/crates/googletest/src/matcher.rs
index 071b0d1..948ea41 100644
--- a/crates/googletest/src/matcher.rs
+++ b/crates/googletest/src/matcher.rs
@@ -15,24 +15,35 @@
//! The components required to implement matchers.
use crate::description::Description;
-use crate::internal::source_location::SourceLocation;
use crate::internal::test_outcome::TestAssertionFailure;
use crate::matchers::__internal_unstable_do_not_depend_on_these::ConjunctionMatcher;
use crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatcher;
+pub use googletest_macro::MatcherBase;
use std::fmt::Debug;
/// An interface for checking an arbitrary condition on a datum.
-pub trait Matcher {
- /// The type against which this matcher matches.
- type ActualT: Debug + ?Sized;
+///
+/// This trait is automatically implemented for a reference of any type
+/// implementing `Matcher`. This simplifies reusing a matcher in different
+/// assertions.
+///
+/// It is also implemented for tuple of `Matcher`. If `MatcherT: Matcher<T>` and
+/// `MatcherU: Matcher<U>`, then `(MatcherT, MatcherU): Matcher<(T, U)>`, and so
+/// on, up to 12 elements. Tuples longer than that do not automatically inherit
+/// the `Debug` trait from their members, so are generally not well-supported;
+/// see [Rust by Example](https://doc.rust-lang.org/rust-by-example/primitives/tuples.html#tuples).
+// `ActualT` requires `Copy` so that `actual` could be passed to `matches` and
+// if it fails passed to `explain_match`. We can relax this constraint later by
+// requiring only `Clone`.
+pub trait Matcher<ActualT: Debug + Copy>: MatcherBase {
/// Returns whether the condition matches the datum `actual`.
///
/// The trait implementation defines what it means to "match". Often the
/// matching condition is based on data stored in the matcher. For example,
/// `eq` matches when its stored expected value is equal (in the sense of
/// the `==` operator) to the value `actual`.
- fn matches(&self, actual: &Self::ActualT) -> MatcherResult;
+ fn matches(&self, actual: ActualT) -> MatcherResult;
/// Returns a description of `self` or a negative description if
/// `matcher_result` is `DoesNotMatch`.
@@ -117,8 +128,8 @@
/// inner matcher and appears as follows:
///
/// ```ignore
- /// fn explain_match(&self, actual: &Self::ActualT) -> Description {
- /// self.expected.explain_match(actual.deref())
+ /// fn explain_match(&self, actual: &ActualT) -> Description {
+ /// self.expected.explain_match(*actual)
/// }
/// ```
///
@@ -127,16 +138,30 @@
/// inner matcher at a point where a relative clause would fit. For example:
///
/// ```ignore
- /// fn explain_match(&self, actual: &Self::ActualT) -> Description {
+ /// fn explain_match(&self, actual: ActualT) -> Description {
/// Description::new()
/// .text("which points to a value")
- /// .nested(self.expected.explain_match(actual.deref()))
+ /// .nested(self.expected.explain_match(*actual))
/// }
/// ```
- fn explain_match(&self, actual: &Self::ActualT) -> Description {
+ fn explain_match(&self, actual: ActualT) -> Description {
format!("which {}", self.describe(self.matches(actual))).into()
}
+}
+/// Base trait for matchers. Any type implementing `Matcher` must implement
+/// `MatcherBase`, but that should be done through the `#[derive(MatcherBase)]`
+/// macro.
+// The `and` and `or` functions cannot be part of the `Matcher` trait since it
+// can be implemented multiple times for a given matcher type. Consider that
+// `and` and `or` are part of the `Matcher` trait and `MyMatcher` implements
+// both `Matcher<A>` and `Matcher<B>`. Then `MyMatcher{...}.and(...)` can be
+// either:
+// * `Matcher::<A>::and(MyMatcher{...}, ...)` or
+// * `Matcher::<B>::and(MyMatcher{...}, ...)`.
+// Moving the `and` and `or` functions in a non-generic trait removes this
+// confusion by making `and` and `or` unique for a given type.
+pub trait MatcherBase {
/// Constructs a matcher that matches both `self` and `right`.
///
/// ```
@@ -160,10 +185,7 @@
// TODO(b/264518763): Replace the return type with impl Matcher and reduce
// visibility of ConjunctionMatcher once impl in return position in trait
// methods is stable.
- fn and<Right: Matcher<ActualT = Self::ActualT>>(
- self,
- right: Right,
- ) -> ConjunctionMatcher<Self, Right>
+ fn and<Right>(self, right: Right) -> ConjunctionMatcher<Self, Right>
where
Self: Sized,
{
@@ -190,10 +212,7 @@
// TODO(b/264518763): Replace the return type with impl Matcher and reduce
// visibility of DisjunctionMatcher once impl in return position in trait
// methods is stable.
- fn or<Right: Matcher<ActualT = Self::ActualT>>(
- self,
- right: Right,
- ) -> DisjunctionMatcher<Self, Right>
+ fn or<Right>(self, right: Right) -> DisjunctionMatcher<Self, Right>
where
Self: Sized,
{
@@ -210,11 +229,11 @@
///
/// The parameter `actual_expr` contains the expression which was evaluated to
/// obtain `actual`.
-pub(crate) fn create_assertion_failure<T: Debug + ?Sized>(
- matcher: &impl Matcher<ActualT = T>,
- actual: &T,
+#[track_caller]
+pub(crate) fn create_assertion_failure<T: Debug + Copy>(
+ matcher: &impl Matcher<T>,
+ actual: T,
actual_expr: &'static str,
- source_location: SourceLocation,
) -> TestAssertionFailure {
let actual_formatted = format!("{actual:?}");
let actual_formatted = if actual_formatted.len() > PRETTY_PRINT_LENGTH_THRESHOLD {
@@ -227,8 +246,7 @@
Value of: {actual_expr}
Expected: {}
Actual: {actual_formatted},
-{}
-{source_location}",
+{}",
matcher.describe(MatcherResult::Match),
matcher.explain_match(actual).indent(),
))
@@ -245,7 +263,11 @@
impl From<bool> for MatcherResult {
fn from(b: bool) -> Self {
- if b { MatcherResult::Match } else { MatcherResult::NoMatch }
+ if b {
+ MatcherResult::Match
+ } else {
+ MatcherResult::NoMatch
+ }
}
}
@@ -268,3 +290,39 @@
matches!(self, MatcherResult::NoMatch)
}
}
+
+impl<M: ?Sized + MatcherBase> MatcherBase for &M {}
+
+impl<T: Debug + Copy, M: Matcher<T>> Matcher<T> for &M {
+ fn matches(&self, actual: T) -> MatcherResult {
+ (*self).matches(actual)
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ (*self).describe(matcher_result)
+ }
+
+ fn explain_match(&self, actual: T) -> Description {
+ (*self).explain_match(actual)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::prelude::*;
+
+ #[test]
+ fn ref_matchers_can_be_reused() -> Result<()> {
+ let matcher = eq(1);
+
+ verify_that!(1, &matcher)?;
+ verify_that!(1, &matcher)
+ }
+
+ #[test]
+ fn ref_matchers_as_inner_matcher() -> Result<()> {
+ let matcher = gt(1);
+
+ verify_that!([2, 3, 4, 5], [&matcher, &matcher, &matcher, &matcher])
+ }
+}
diff --git a/crates/googletest/src/matcher_support/auto_eq.rs b/crates/googletest/src/matcher_support/auto_eq.rs
new file mode 100644
index 0000000..16bfde3
--- /dev/null
+++ b/crates/googletest/src/matcher_support/auto_eq.rs
@@ -0,0 +1,106 @@
+// Copyright 2024 Google LLC
+//
+// 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.
+
+#![doc(hidden)]
+
+/// Macro that wraps the expression with `eq(...)` if the expression is
+/// not a matcher.
+///
+/// This is useful to let users pass expected value to macro matchers like
+/// `field!` and `property!`.
+///`
+/// **For internal use only. API stablility is not guaranteed!**
+/// If you are interested in using it in your matcher, please file an issue to
+/// stabilize this.
+#[macro_export]
+macro_rules! __auto_eq {
+ ($e:expr) => {{
+ #[allow(unused_imports)]
+ use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::ExpectedKind as _;
+ match $e {
+ expected => {
+ $crate::matcher_support::__internal_unstable_do_not_depend_on_these::Wrapper(
+ &expected,
+ )
+ .kind()
+ .matcher(expected)
+ }
+ }
+ }};
+}
+
+// This reimplements the pattern presented in
+// https://github.com/dtolnay/case-studies/issues/14
+pub mod internal {
+ use crate::{
+ matcher::MatcherBase,
+ matchers::{eq, EqMatcher},
+ };
+
+ pub struct Wrapper<T>(pub T);
+
+ impl<'a, T: MatcherBase> Wrapper<&'a T> {
+ #[inline]
+ pub fn kind(&self) -> MatcherTag {
+ MatcherTag
+ }
+ }
+
+ pub trait ExpectedKind {
+ #[inline]
+ fn kind(&self) -> ExpectedTag {
+ ExpectedTag
+ }
+ }
+
+ impl<T> ExpectedKind for Wrapper<T> {}
+
+ pub struct MatcherTag;
+
+ impl MatcherTag {
+ #[inline]
+ pub fn matcher<M>(self, matcher: M) -> M {
+ matcher
+ }
+ }
+ pub struct ExpectedTag;
+
+ impl ExpectedTag {
+ #[inline]
+ pub fn matcher<T>(self, expected: T) -> EqMatcher<T> {
+ eq(expected)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::prelude::*;
+
+ #[test]
+ fn auto_ref_matcher() -> Result<()> {
+ verify_that!(123, __auto_eq!(ge(9)))
+ }
+
+ #[test]
+ fn auto_ref_expected() -> Result<()> {
+ verify_that!(123, __auto_eq!(123))
+ }
+
+ #[test]
+ fn auto_ref_on_ref_matcher() -> Result<()> {
+ let matcher = eq(123);
+ verify_that!(123, __auto_eq!(&matcher))
+ }
+}
diff --git a/crates/googletest/src/matcher_support/count_elements.rs b/crates/googletest/src/matcher_support/count_elements.rs
index 662dcc9..6d6b8e6 100644
--- a/crates/googletest/src/matcher_support/count_elements.rs
+++ b/crates/googletest/src/matcher_support/count_elements.rs
@@ -18,10 +18,7 @@
/// unambiguous answer, i.e., the upper bound exists and the lower and upper
/// bounds agree. Otherwise it iterates through `value` and counts the
/// elements.
-pub(crate) fn count_elements<ContainerT: ?Sized>(value: &ContainerT) -> usize
-where
- for<'b> &'b ContainerT: IntoIterator,
-{
+pub(crate) fn count_elements<ContainerT: IntoIterator>(value: ContainerT) -> usize {
let iterator = value.into_iter();
if let (lower, Some(higher)) = iterator.size_hint() {
if lower == higher {
@@ -30,3 +27,53 @@
}
iterator.count()
}
+
+#[cfg(test)]
+mod tests {
+
+ use super::*;
+ use crate::prelude::*;
+
+ #[test]
+ fn count_elements_vec() -> Result<()> {
+ verify_that!(count_elements(vec![1, 2, 3]), eq(3))
+ }
+
+ #[test]
+ fn count_elements_with_imprecise_hint() -> Result<()> {
+ struct FakeIterator;
+
+ impl Iterator for FakeIterator {
+ type Item = ();
+
+ fn next(&mut self) -> Option<Self::Item> {
+ None
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ (0, Some(123))
+ }
+ }
+
+ verify_that!(count_elements(FakeIterator), eq(0))
+ }
+
+ #[test]
+ fn count_elements_with_no_hint() -> Result<()> {
+ struct FakeIterator;
+
+ impl Iterator for FakeIterator {
+ type Item = ();
+
+ fn next(&mut self) -> Option<Self::Item> {
+ None
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ (0, None)
+ }
+ }
+
+ verify_that!(count_elements(FakeIterator), eq(0))
+ }
+}
diff --git a/crates/googletest/src/matcher_support/edit_distance.rs b/crates/googletest/src/matcher_support/edit_distance.rs
index 8847bc9..f5e1b9a 100644
--- a/crates/googletest/src/matcher_support/edit_distance.rs
+++ b/crates/googletest/src/matcher_support/edit_distance.rs
@@ -289,7 +289,7 @@
#[test]
fn returns_equal_when_strings_are_equal() -> Result<()> {
let result = edit_list(["A string"], ["A string"], Mode::Exact);
- verify_that!(result, matches_pattern!(Difference::Equal))
+ verify_that!(result, matches_pattern!(&Difference::Equal))
}
#[test]
@@ -299,7 +299,7 @@
["A string (1)", "A string (2)"],
Mode::Exact,
);
- verify_that!(result, matches_pattern!(Difference::Equal))
+ verify_that!(result, matches_pattern!(&Difference::Equal))
}
#[test]
@@ -307,8 +307,8 @@
let result = edit_list(["A string"], [], Mode::Exact);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![matches_pattern!(
- Edit::ExtraActual(eq("A string"))
+ matches_pattern!(&Difference::Editable(ref elements_are![matches_pattern!(
+ &Edit::ExtraActual(eq("A string"))
)]))
)
}
@@ -318,8 +318,8 @@
let result = edit_list([], ["A string"], Mode::Exact);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![matches_pattern!(
- Edit::ExtraExpected(eq("A string"))
+ matches_pattern!(&Difference::Editable(ref elements_are![matches_pattern!(
+ &Edit::ExtraExpected(eq("A string"))
)]))
)
}
@@ -329,9 +329,9 @@
let result = edit_list(["A string"], ["Another string"], Mode::Exact);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![
- matches_pattern!(Edit::ExtraActual(eq("A string"))),
- matches_pattern!(Edit::ExtraExpected(eq("Another string"))),
+ matches_pattern!(&Difference::Editable(ref elements_are![
+ matches_pattern!(&Edit::ExtraActual(eq("A string"))),
+ matches_pattern!(&Edit::ExtraExpected(eq("Another string"))),
]))
)
}
@@ -342,11 +342,11 @@
edit_list(["A string", "A string"], ["Another string", "Another string"], Mode::Exact);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![
- matches_pattern!(Edit::ExtraActual(eq("A string"))),
- matches_pattern!(Edit::ExtraExpected(eq("Another string"))),
- matches_pattern!(Edit::ExtraActual(eq("A string"))),
- matches_pattern!(Edit::ExtraExpected(eq("Another string"))),
+ matches_pattern!(&Difference::Editable(ref elements_are![
+ matches_pattern!(&Edit::ExtraActual(eq("A string"))),
+ matches_pattern!(&Edit::ExtraExpected(eq("Another string"))),
+ matches_pattern!(&Edit::ExtraActual(eq("A string"))),
+ matches_pattern!(&Edit::ExtraExpected(eq("Another string"))),
]))
)
}
@@ -360,10 +360,10 @@
);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![
- matches_pattern!(Edit::Both(eq("Common part"))),
- matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
- matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
+ matches_pattern!(&Difference::Editable(ref elements_are![
+ matches_pattern!(&Edit::Both(eq("Common part"))),
+ matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
+ matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
]))
)
}
@@ -373,9 +373,9 @@
let result = edit_list(["Common part", "Actual only"], ["Common part"], Mode::Exact);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![
- matches_pattern!(Edit::Both(eq("Common part"))),
- matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
+ matches_pattern!(&Difference::Editable(ref elements_are![
+ matches_pattern!(&Edit::Both(eq("Common part"))),
+ matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
]))
)
}
@@ -385,9 +385,9 @@
let result = edit_list(["Common part"], ["Common part", "Expected only"], Mode::Exact);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![
- matches_pattern!(Edit::Both(eq("Common part"))),
- matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
+ matches_pattern!(&Difference::Editable(ref elements_are![
+ matches_pattern!(&Edit::Both(eq("Common part"))),
+ matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
]))
)
}
@@ -401,10 +401,10 @@
);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![
- matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
- matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
- matches_pattern!(Edit::Both(eq("Common part"))),
+ matches_pattern!(&Difference::Editable(ref elements_are![
+ matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
+ matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
+ matches_pattern!(&Edit::Both(eq("Common part"))),
]))
)
}
@@ -419,19 +419,19 @@
);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![
- matches_pattern!(Edit::ExtraActual(eq("Actual only (1)"))),
- matches_pattern!(Edit::ExtraExpected(eq("Expected only (1)"))),
- matches_pattern!(Edit::Both(eq("Common part"))),
- matches_pattern!(Edit::ExtraActual(eq("Actual only (2)"))),
- matches_pattern!(Edit::ExtraExpected(eq("Expected only (2)"))),
+ matches_pattern!(&Difference::Editable(ref elements_are![
+ matches_pattern!(&Edit::ExtraActual(eq("Actual only (1)"))),
+ matches_pattern!(&Edit::ExtraExpected(eq("Expected only (1)"))),
+ matches_pattern!(&Edit::Both(eq("Common part"))),
+ matches_pattern!(&Edit::ExtraActual(eq("Actual only (2)"))),
+ matches_pattern!(&Edit::ExtraExpected(eq("Expected only (2)"))),
]))
)
}
#[test]
- fn returns_common_part_plus_difference_plus_common_part_when_there_is_common_prefix_and_suffix()
- -> Result<()> {
+ fn returns_common_part_plus_difference_plus_common_part_when_there_is_common_prefix_and_suffix(
+ ) -> Result<()> {
let result = edit_list(
["Common part (1)", "Actual only", "Common part (2)"],
["Common part (1)", "Expected only", "Common part (2)"],
@@ -439,18 +439,18 @@
);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![
- matches_pattern!(Edit::Both(eq("Common part (1)"))),
- matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
- matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
- matches_pattern!(Edit::Both(eq("Common part (2)"))),
+ matches_pattern!(&Difference::Editable(ref elements_are![
+ matches_pattern!(&Edit::Both(eq("Common part (1)"))),
+ matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
+ matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
+ matches_pattern!(&Edit::Both(eq("Common part (2)"))),
]))
)
}
#[test]
- fn returns_common_part_plus_extra_actual_plus_common_part_when_there_is_common_prefix_and_suffix()
- -> Result<()> {
+ fn returns_common_part_plus_extra_actual_plus_common_part_when_there_is_common_prefix_and_suffix(
+ ) -> Result<()> {
let result = edit_list(
["Common part (1)", "Actual only", "Common part (2)"],
["Common part (1)", "Common part (2)"],
@@ -458,17 +458,17 @@
);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![
- matches_pattern!(Edit::Both(eq("Common part (1)"))),
- matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
- matches_pattern!(Edit::Both(eq("Common part (2)"))),
+ matches_pattern!(&Difference::Editable(ref elements_are![
+ matches_pattern!(&Edit::Both(eq("Common part (1)"))),
+ matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
+ matches_pattern!(&Edit::Both(eq("Common part (2)"))),
]))
)
}
#[test]
- fn returns_common_part_plus_extra_expected_plus_common_part_when_there_is_common_prefix_and_suffix()
- -> Result<()> {
+ fn returns_common_part_plus_extra_expected_plus_common_part_when_there_is_common_prefix_and_suffix(
+ ) -> Result<()> {
let result = edit_list(
["Common part (1)", "Common part (2)"],
["Common part (1)", "Expected only", "Common part (2)"],
@@ -476,10 +476,10 @@
);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![
- matches_pattern!(Edit::Both(eq("Common part (1)"))),
- matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
- matches_pattern!(Edit::Both(eq("Common part (2)"))),
+ matches_pattern!(&Difference::Editable(ref elements_are![
+ matches_pattern!(&Edit::Both(eq("Common part (1)"))),
+ matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
+ matches_pattern!(&Edit::Both(eq("Common part (2)"))),
]))
)
}
@@ -493,15 +493,15 @@
);
verify_that!(
result,
- matches_pattern!(Difference::Editable(not(contains(matches_pattern!(
- Edit::ExtraActual(eq("Actual only"))
+ matches_pattern!(&Difference::Editable(ref not(contains(matches_pattern!(
+ &Edit::ExtraActual(eq("Actual only"))
)))))
)
}
#[test]
- fn does_not_skip_extra_parts_on_actual_in_prefix_mode_at_end_when_they_are_in_common()
- -> Result<()> {
+ fn does_not_skip_extra_parts_on_actual_in_prefix_mode_at_end_when_they_are_in_common(
+ ) -> Result<()> {
let result = edit_list(
["Actual only", "Common part"],
["Expected only", "Common part"],
@@ -509,24 +509,24 @@
);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![
- matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
- matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
- matches_pattern!(Edit::Both(eq("Common part"))),
+ matches_pattern!(&Difference::Editable(ref elements_are![
+ matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
+ matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
+ matches_pattern!(&Edit::Both(eq("Common part"))),
]))
)
}
#[test]
- fn does_not_skip_corresponding_line_on_actual_when_actual_and_expected_differ_in_prefix_mode()
- -> Result<()> {
+ fn does_not_skip_corresponding_line_on_actual_when_actual_and_expected_differ_in_prefix_mode(
+ ) -> Result<()> {
let result = edit_list(["Actual only"], ["Expected only"], Mode::Prefix);
verify_that!(
result,
- matches_pattern!(Difference::Editable(elements_are![
- matches_pattern!(Edit::ExtraActual(eq("Actual only"))),
- matches_pattern!(Edit::ExtraExpected(eq("Expected only"))),
- matches_pattern!(Edit::AdditionalActual),
+ matches_pattern!(&Difference::Editable(ref elements_are![
+ matches_pattern!(&Edit::ExtraActual(eq("Actual only"))),
+ matches_pattern!(&Edit::ExtraExpected(eq("Expected only"))),
+ matches_pattern!(&Edit::AdditionalActual),
]))
)
}
@@ -534,7 +534,7 @@
#[test]
fn returns_unrelated_when_maximum_distance_exceeded() -> Result<()> {
let result = edit_list(0..=50, 60..110, Mode::Exact);
- verify_that!(result, matches_pattern!(Difference::Unrelated))
+ verify_that!(result, matches_pattern!(&Difference::Unrelated))
}
quickcheck! {
diff --git a/crates/googletest/src/matcher_support/mod.rs b/crates/googletest/src/matcher_support/mod.rs
index 8a72ba4..8e138a1 100644
--- a/crates/googletest/src/matcher_support/mod.rs
+++ b/crates/googletest/src/matcher_support/mod.rs
@@ -18,7 +18,13 @@
//! these facilities could be useful to downstream users writing custom
//! matchers.
+mod auto_eq;
pub(crate) mod count_elements;
pub(crate) mod edit_distance;
pub(crate) mod summarize_diff;
pub(crate) mod zipped_iterator;
+
+pub mod __internal_unstable_do_not_depend_on_these {
+ pub use super::auto_eq::internal::{ExpectedKind, Wrapper};
+ pub use crate::__auto_eq as auto_eq;
+}
diff --git a/crates/googletest/src/matcher_support/summarize_diff.rs b/crates/googletest/src/matcher_support/summarize_diff.rs
index cd4efa7..b7cf641 100644
--- a/crates/googletest/src/matcher_support/summarize_diff.rs
+++ b/crates/googletest/src/matcher_support/summarize_diff.rs
@@ -17,7 +17,7 @@
use crate::matcher_support::edit_distance;
#[rustversion::since(1.70)]
use std::io::IsTerminal;
-use std::{borrow::Cow, fmt::Display};
+use std::{borrow::Cow, cell::Cell, fmt::Display};
/// Returns a string describing how the expected and actual lines differ.
///
@@ -38,10 +38,29 @@
// line-by-line diff.
return "".into();
}
+
match edit_distance::edit_list(actual_debug.lines(), expected_debug.lines(), diff_mode) {
- edit_distance::Difference::Equal => "No difference found between debug strings.".into(),
+ edit_distance::Difference::Equal => {
+ // str.lines() is oblivious to the last newline in a
+ // string, so we need to check this to make sure we don't spuriously
+ // claim that 'hello' and 'hello\n' are identical debug strings.
+ //
+ // Although we would have liked to resolve by replacing
+ // str::lines() with str::split('\n'), the potentially
+ // empty last element interferes with good diff output for
+ // "contains" checks.
+ let actual_newline_terminated = actual_debug.ends_with('\n');
+ let expected_newline_terminated = expected_debug.ends_with('\n');
+ if actual_newline_terminated && !expected_newline_terminated {
+ "Actual includes a terminating newline that is absent from expected.".into()
+ } else if !actual_newline_terminated && expected_newline_terminated {
+ "Actual omits a terminating newline that is present in expected.".into()
+ } else {
+ "No difference found between debug strings.".into()
+ }
+ }
edit_distance::Difference::Editable(edit_list) => {
- format!("\n{}{}", summary_header(), edit_list.into_iter().collect::<BufferedSummary>(),)
+ format!("{}{}", summary_header(), edit_list.into_iter().collect::<BufferedSummary>(),)
.into()
}
edit_distance::Difference::Unrelated => "".into(),
@@ -73,7 +92,7 @@
edit_distance::Difference::Equal => "No difference found between debug strings.".into(),
edit_distance::Difference::Editable(mut edit_list) => {
edit_list.reverse();
- format!("\n{}{}", summary_header(), edit_list.into_iter().collect::<BufferedSummary>(),)
+ format!("{}{}", summary_header(), edit_list.into_iter().collect::<BufferedSummary>(),)
.into()
}
edit_distance::Difference::Unrelated => "".into(),
@@ -81,9 +100,9 @@
}
// Produces the header, with or without coloring depending on
-// stdout_supports_color()
+// USE_COLOR
fn summary_header() -> Cow<'static, str> {
- if stdout_supports_color() {
+ if USE_COLOR.with(Cell::get) {
format!(
"Difference(-{ACTUAL_ONLY_STYLE}actual{RESET_ALL} / +{EXPECTED_ONLY_STYLE}expected{RESET_ALL}):"
).into()
@@ -105,11 +124,11 @@
impl<'a> BufferedSummary<'a> {
// Appends a new line which is common to both actual and expected.
fn feed_common_lines(&mut self, common_line: &'a str) {
- if let Buffer::CommonLineBuffer(ref mut common_lines) = self.buffer {
+ if let Buffer::CommonLines(ref mut common_lines) = self.buffer {
common_lines.push(common_line);
} else {
self.flush_buffer();
- self.buffer = Buffer::CommonLineBuffer(vec![common_line]);
+ self.buffer = Buffer::CommonLines(vec![common_line]);
}
}
@@ -229,7 +248,7 @@
enum Buffer<'a> {
Empty,
- CommonLineBuffer(Vec<&'a str>),
+ CommonLines(Vec<&'a str>),
ExtraActualLineChunk(&'a str),
ExtraExpectedLineChunk(&'a str),
}
@@ -238,7 +257,7 @@
fn flush(&mut self, summary: &mut SummaryBuilder) {
match self {
Buffer::Empty => {}
- Buffer::CommonLineBuffer(common_lines) => {
+ Buffer::CommonLines(common_lines) => {
Self::flush_common_lines(std::mem::take(common_lines), summary);
}
Buffer::ExtraActualLineChunk(extra_actual) => {
@@ -294,8 +313,13 @@
}
}
+thread_local! {
+ pub(crate) static USE_COLOR: Cell<bool> = Cell::new(stdout_supports_color());
+}
+
#[rustversion::since(1.70)]
fn stdout_supports_color() -> bool {
+ #[allow(clippy::incompatible_msrv)]
match (is_env_var_set("NO_COLOR"), is_env_var_set("FORCE_COLOR")) {
(true, _) => false,
(false, true) => true,
@@ -305,7 +329,7 @@
#[rustversion::not(since(1.70))]
fn stdout_supports_color() -> bool {
- is_env_var_set("FORCE_COLOR")
+ is_env_var_set("FORCE_COLOR") && !is_env_var_set("NO_COLOR")
}
fn is_env_var_set(var: &'static str) -> bool {
@@ -388,14 +412,14 @@
}
fn reset_ansi(&mut self) {
- if !self.last_ansi_style.is_empty() && stdout_supports_color() {
+ if !self.last_ansi_style.is_empty() && USE_COLOR.with(Cell::get) {
self.summary.push_str(RESET_ALL);
self.last_ansi_style = "";
}
}
fn set_ansi(&mut self, ansi_style: &'static str) {
- if !stdout_supports_color() || self.last_ansi_style == ansi_style {
+ if !USE_COLOR.with(Cell::get) || self.last_ansi_style == ansi_style {
return;
}
if !self.last_ansi_style.is_empty() {
@@ -411,7 +435,6 @@
use super::*;
use crate::{matcher_support::edit_distance::Mode, prelude::*};
use indoc::indoc;
- use serial_test::{parallel, serial};
use std::fmt::Write;
// Make a long text with each element of the iterator on one line.
@@ -427,13 +450,11 @@
}
#[test]
- #[parallel]
fn create_diff_smaller_than_one_line() -> Result<()> {
verify_that!(create_diff("One", "Two", Mode::Exact), eq(""))
}
#[test]
- #[parallel]
fn create_diff_exact_same() -> Result<()> {
let expected = indoc! {"
One
@@ -450,7 +471,6 @@
}
#[test]
- #[parallel]
fn create_diff_multiline_diff() -> Result<()> {
let expected = indoc! {"
prefix
@@ -469,7 +489,6 @@
create_diff(expected, actual, Mode::Exact),
eq(indoc!(
"
-
Difference(-actual / +expected):
prefix
-Actual#1
@@ -483,19 +502,16 @@
}
#[test]
- #[parallel]
fn create_diff_exact_unrelated() -> Result<()> {
verify_that!(create_diff(&build_text(1..500), &build_text(501..1000), Mode::Exact), eq(""))
}
#[test]
- #[parallel]
fn create_diff_exact_small_difference() -> Result<()> {
verify_that!(
create_diff(&build_text(1..50), &build_text(1..51), Mode::Exact),
eq(indoc! {
"
-
Difference(-actual / +expected):
1
2
@@ -507,33 +523,14 @@
)
}
- // Test with color enabled.
-
- struct ForceColor;
-
- fn force_color() -> ForceColor {
- std::env::set_var("FORCE_COLOR", "1");
- std::env::remove_var("NO_COLOR");
- ForceColor
- }
-
- impl Drop for ForceColor {
- fn drop(&mut self) {
- std::env::remove_var("FORCE_COLOR");
- std::env::set_var("NO_COLOR", "1");
- }
- }
-
#[test]
- #[serial]
fn create_diff_exact_small_difference_with_color() -> Result<()> {
- let _keep = force_color();
+ USE_COLOR.with(|cell| cell.set(true));
verify_that!(
create_diff(&build_text(1..50), &build_text(1..51), Mode::Exact),
eq(indoc! {
"
-
Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
1
2
@@ -546,9 +543,9 @@
}
#[test]
- #[serial]
fn create_diff_exact_difference_with_inline_color() -> Result<()> {
- let _keep = force_color();
+ USE_COLOR.with(|cell| cell.set(true));
+
let actual = indoc!(
"There is a home in Nouvelle Orleans
They say, it is the rising sons
@@ -565,7 +562,6 @@
create_diff(actual, expected, Mode::Exact),
eq(indoc! {
"
-
Difference(-\x1B[1;31mactual\x1B[0m / +\x1B[1;32mexpected\x1B[0m):
-\x1B[31mThere is a ho\x1B[0m\x1B[1;31mm\x1B[0m\x1B[31me in N\x1B[0m\x1B[1;31mouv\x1B[0m\x1B[31me\x1B[0m\x1B[1;31mlle\x1B[0m\x1B[31m Orleans\x1B[0m
+\x1B[32mThere is a ho\x1B[0m\x1B[1;32mus\x1B[0m\x1B[32me \x1B[0m\x1B[1;32mway down \x1B[0m\x1B[32min Ne\x1B[0m\x1B[1;32mw\x1B[0m\x1B[32m Orleans\x1B[0m
@@ -576,4 +572,20 @@
})
)
}
+
+ #[test]
+ fn create_diff_line_termination_diff() -> Result<()> {
+ verify_that!(
+ create_diff("1\n2\n3", "1\n2\n3\n", Mode::Exact),
+ eq("Actual omits a terminating newline that is present in expected.")
+ )?;
+ verify_that!(
+ create_diff("1\n2\n3\n", "1\n2\n3", Mode::Exact),
+ eq("Actual includes a terminating newline that is absent from expected.")
+ )?;
+ verify_that!(
+ create_diff("1\n2\n3\n", "1\n2\n3\n", Mode::Exact),
+ eq("No difference found between debug strings.")
+ )
+ }
}
diff --git a/crates/googletest/src/matchers/all_matcher.rs b/crates/googletest/src/matchers/all_matcher.rs
index f2e6d06..d03e7d4 100644
--- a/crates/googletest/src/matchers/all_matcher.rs
+++ b/crates/googletest/src/matchers/all_matcher.rs
@@ -38,7 +38,7 @@
/// ```
///
/// Using this macro is equivalent to using the
-/// [`and`][crate::matcher::Matcher::and] method:
+/// [`and`][crate::matcher::MatcherBase::and] method:
///
/// ```
/// # use googletest::prelude::*;
@@ -50,109 +50,41 @@
/// ```
///
/// Assertion failure messages are not guaranteed to be identical, however.
+///
+/// If an inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+///
+/// verify_that!(123, all![123, lt(1000), gt(100)])
+/// # .unwrap();
+/// ```
#[macro_export]
#[doc(hidden)]
macro_rules! __all {
- ($($matcher:expr),* $(,)?) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::AllMatcher;
- AllMatcher::new([$(Box::new($matcher)),*])
+ ($(,)?) => {{
+ $crate::matchers::anything()
+ }} ;
+ ($matcher:expr $(,)?) => {{
+ use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
+ auto_eq!($matcher)
+ }};
+ ($head:expr, $head2:expr $(,)?) => {{
+ use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::ConjunctionMatcher::new(auto_eq!($head), auto_eq!($head2))
+ }};
+ ($head:expr, $head2:expr, $($tail:expr),+ $(,)?) => {{
+ use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
+ $crate::__all![
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::ConjunctionMatcher::new(auto_eq!($head), auto_eq!($head2)),
+ $($tail),+
+ ]
}}
}
-/// Functionality needed by the [`all`] macro.
-///
-/// For internal use only. API stablility is not guaranteed!
-#[doc(hidden)]
-pub mod internal {
- use crate::description::Description;
- use crate::matcher::{Matcher, MatcherResult};
- use crate::matchers::anything;
- use std::fmt::Debug;
-
- /// A matcher which matches an input value matched by all matchers in the
- /// array `components`.
- ///
- /// For internal use only. API stablility is not guaranteed!
- #[doc(hidden)]
- pub struct AllMatcher<'a, T: Debug + ?Sized, const N: usize> {
- components: [Box<dyn Matcher<ActualT = T> + 'a>; N],
- }
-
- impl<'a, T: Debug + ?Sized, const N: usize> AllMatcher<'a, T, N> {
- /// Constructs an [`AllMatcher`] with the given component matchers.
- ///
- /// Intended for use only by the [`all`] macro.
- pub fn new(components: [Box<dyn Matcher<ActualT = T> + 'a>; N]) -> Self {
- Self { components }
- }
- }
-
- impl<'a, T: Debug + ?Sized, const N: usize> Matcher for AllMatcher<'a, T, N> {
- type ActualT = T;
-
- fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
- for component in &self.components {
- match component.matches(actual) {
- MatcherResult::NoMatch => {
- return MatcherResult::NoMatch;
- }
- MatcherResult::Match => {}
- }
- }
- MatcherResult::Match
- }
-
- fn explain_match(&self, actual: &Self::ActualT) -> Description {
- match N {
- 0 => anything::<T>().explain_match(actual),
- 1 => self.components[0].explain_match(actual),
- _ => {
- let failures = self
- .components
- .iter()
- .filter(|component| component.matches(actual).is_no_match())
- .collect::<Vec<_>>();
-
- if failures.len() == 1 {
- failures[0].explain_match(actual)
- } else {
- Description::new()
- .collect(
- failures
- .into_iter()
- .map(|component| component.explain_match(actual)),
- )
- .bullet_list()
- }
- }
- }
- }
-
- fn describe(&self, matcher_result: MatcherResult) -> Description {
- match N {
- 0 => anything::<T>().describe(matcher_result),
- 1 => self.components[0].describe(matcher_result),
- _ => {
- let header = if matcher_result.into() {
- "has all the following properties:"
- } else {
- "has at least one of the following properties:"
- };
- Description::new().text(header).nested(
- Description::new()
- .bullet_list()
- .collect(self.components.iter().map(|m| m.describe(matcher_result))),
- )
- }
- }
- }
- }
-}
-
#[cfg(test)]
mod tests {
- use super::internal;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
@@ -160,10 +92,10 @@
fn description_shows_more_than_one_matcher() -> Result<()> {
let first_matcher = starts_with("A");
let second_matcher = ends_with("string");
- let matcher: internal::AllMatcher<String, 2> = all!(first_matcher, second_matcher);
+ let matcher = all!(first_matcher, second_matcher);
verify_that!(
- matcher.describe(MatcherResult::Match),
+ Matcher::<&String>::describe(&matcher, MatcherResult::Match),
displays_as(eq(indoc!(
"
has all the following properties:
@@ -176,10 +108,10 @@
#[test]
fn description_shows_one_matcher_directly() -> Result<()> {
let first_matcher = starts_with("A");
- let matcher: internal::AllMatcher<String, 1> = all!(first_matcher);
+ let matcher = all!(first_matcher);
verify_that!(
- matcher.describe(MatcherResult::Match),
+ Matcher::<&String>::describe(&matcher, MatcherResult::Match),
displays_as(eq("starts with prefix \"A\""))
)
}
@@ -189,7 +121,7 @@
{
let first_matcher = starts_with("Another");
let second_matcher = ends_with("string");
- let matcher: internal::AllMatcher<str, 2> = all!(first_matcher, second_matcher);
+ let matcher = all!(first_matcher, second_matcher);
verify_that!(
matcher.explain_match("A string"),
@@ -200,11 +132,16 @@
#[test]
fn mismatch_description_is_simple_when_only_one_consistuent() -> Result<()> {
let first_matcher = starts_with("Another");
- let matcher: internal::AllMatcher<str, 1> = all!(first_matcher);
+ let matcher = all!(first_matcher);
verify_that!(
matcher.explain_match("A string"),
displays_as(eq("which does not start with \"Another\""))
)
}
+
+ #[test]
+ fn all_with_auto_eq() -> Result<()> {
+ verify_that!(42, all![eq(42), 42, lt(100)])
+ }
}
diff --git a/crates/googletest/src/matchers/any_matcher.rs b/crates/googletest/src/matchers/any_matcher.rs
index 95d53fe..6f988c8 100644
--- a/crates/googletest/src/matchers/any_matcher.rs
+++ b/crates/googletest/src/matchers/any_matcher.rs
@@ -40,7 +40,7 @@
/// ```
///
/// Using this macro is equivalent to using the
-/// [`or`][crate::matcher::Matcher::or] method:
+/// [`or`][crate::matcher::MatcherBase::or] method:
///
/// ```
/// # use googletest::prelude::*;
@@ -52,107 +52,41 @@
/// ```
///
/// Assertion failure messages are not guaranteed to be identical, however.
+///
+/// If an inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+///
+/// verify_that!(123, any![lt(1), 123, gt(1000)])
+/// # .unwrap();
+/// ```
#[macro_export]
#[doc(hidden)]
macro_rules! __any {
- ($($matcher:expr),* $(,)?) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::AnyMatcher;
- AnyMatcher::new([$(Box::new($matcher)),*])
+ ($(,)?) => {{
+ $crate::matchers::not($crate::matchers::anything())
+ }} ;
+ ($matcher:expr $(,)?) => {{
+ use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
+ auto_eq!($matcher)
+ }};
+ ($head:expr, $head2:expr $(,)?) => {{
+ use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatcher::new(auto_eq!($head), auto_eq!($head2))
+ }};
+ ($head:expr, $head2:expr, $($tail:expr),+ $(,)?) => {{
+ use $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq;
+ $crate::__any![
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatcher::new(auto_eq!($head), auto_eq!($head2)),
+ $($tail),+
+ ]
}}
}
-/// Functionality needed by the [`any`] macro.
-///
-/// For internal use only. API stablility is not guaranteed!
-#[doc(hidden)]
-pub mod internal {
- use crate::description::Description;
- use crate::matcher::{Matcher, MatcherResult};
- use crate::matchers::anything;
- use std::fmt::Debug;
-
- /// A matcher which matches an input value matched by all matchers in the
- /// array `components`.
- ///
- /// For internal use only. API stablility is not guaranteed!
- #[doc(hidden)]
- pub struct AnyMatcher<'a, T: Debug + ?Sized, const N: usize> {
- components: [Box<dyn Matcher<ActualT = T> + 'a>; N],
- }
-
- impl<'a, T: Debug + ?Sized, const N: usize> AnyMatcher<'a, T, N> {
- /// Constructs an [`AnyMatcher`] with the given component matchers.
- ///
- /// Intended for use only by the [`all`] macro.
- pub fn new(components: [Box<dyn Matcher<ActualT = T> + 'a>; N]) -> Self {
- Self { components }
- }
- }
-
- impl<'a, T: Debug + ?Sized, const N: usize> Matcher for AnyMatcher<'a, T, N> {
- type ActualT = T;
-
- fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
- MatcherResult::from(self.components.iter().any(|c| c.matches(actual).is_match()))
- }
-
- fn explain_match(&self, actual: &Self::ActualT) -> Description {
- match N {
- 0 => format!("which {}", anything::<T>().describe(MatcherResult::NoMatch)).into(),
- 1 => self.components[0].explain_match(actual),
- _ => {
- let failures = self
- .components
- .iter()
- .filter(|component| component.matches(actual).is_no_match())
- .collect::<Vec<_>>();
-
- if failures.len() == 1 {
- failures[0].explain_match(actual)
- } else {
- Description::new()
- .collect(
- failures
- .into_iter()
- .map(|component| component.explain_match(actual)),
- )
- .bullet_list()
- }
- }
- }
- }
-
- fn describe(&self, matcher_result: MatcherResult) -> Description {
- match N {
- 0 => anything::<T>().describe(matcher_result),
- 1 => self.components[0].describe(matcher_result),
- _ => {
- let properties = self
- .components
- .iter()
- .map(|m| m.describe(matcher_result))
- .collect::<Description>()
- .bullet_list()
- .indent();
- format!(
- "{}:\n{properties}",
- if matcher_result.into() {
- "has at least one of the following properties"
- } else {
- "has none of the following properties"
- }
- )
- .into()
- }
- }
- }
- }
-}
-
#[cfg(test)]
mod tests {
- use super::internal;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
@@ -160,10 +94,10 @@
fn description_shows_more_than_one_matcher() -> Result<()> {
let first_matcher = starts_with("A");
let second_matcher = ends_with("string");
- let matcher: internal::AnyMatcher<String, 2> = any!(first_matcher, second_matcher);
+ let matcher = any!(first_matcher, second_matcher);
verify_that!(
- matcher.describe(MatcherResult::Match),
+ Matcher::<&String>::describe(&matcher, MatcherResult::Match),
displays_as(eq(indoc!(
"
has at least one of the following properties:
@@ -176,10 +110,10 @@
#[test]
fn description_shows_one_matcher_directly() -> Result<()> {
let first_matcher = starts_with("A");
- let matcher: internal::AnyMatcher<String, 1> = any!(first_matcher);
+ let matcher = any!(first_matcher);
verify_that!(
- matcher.describe(MatcherResult::Match),
+ Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("starts with prefix \"A\""))
)
}
@@ -189,7 +123,7 @@
{
let first_matcher = starts_with("Another");
let second_matcher = ends_with("string");
- let matcher: internal::AnyMatcher<str, 2> = any!(first_matcher, second_matcher);
+ let matcher = any!(first_matcher, second_matcher);
verify_that!(
matcher.explain_match("A string"),
@@ -200,11 +134,21 @@
#[test]
fn mismatch_description_is_simple_when_only_one_constituent() -> Result<()> {
let first_matcher = starts_with("Another");
- let matcher: internal::AnyMatcher<str, 1> = any!(first_matcher);
+ let matcher = any!(first_matcher);
verify_that!(
matcher.explain_match("A string"),
displays_as(eq("which does not start with \"Another\""))
)
}
+
+ #[test]
+ fn empty_any_matcher_never_matches() -> Result<()> {
+ verify_that!(123, not(any![]))
+ }
+
+ #[test]
+ fn any_with_auto_eq() -> Result<()> {
+ verify_that!(42, any![1, 2, 42, gt(123)])
+ }
}
diff --git a/crates/googletest/src/matchers/anything_matcher.rs b/crates/googletest/src/matchers/anything_matcher.rs
index 36be478..a920974 100644
--- a/crates/googletest/src/matchers/anything_matcher.rs
+++ b/crates/googletest/src/matchers/anything_matcher.rs
@@ -14,9 +14,9 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches anything. This matcher always succeeds.
///
@@ -32,16 +32,15 @@
/// # }
/// # should_pass().unwrap();
/// ```
-pub fn anything<T: Debug + ?Sized>() -> impl Matcher<ActualT = T> {
- Anything::<T>(Default::default())
+pub fn anything() -> Anything {
+ Anything
}
-struct Anything<T: ?Sized>(PhantomData<T>);
+#[derive(MatcherBase)]
+pub struct Anything;
-impl<T: Debug + ?Sized> Matcher for Anything<T> {
- type ActualT = T;
-
- fn matches(&self, _: &T) -> MatcherResult {
+impl<T: Debug + Copy> Matcher<T> for Anything {
+ fn matches(&self, _: T) -> MatcherResult {
MatcherResult::Match
}
@@ -55,7 +54,6 @@
#[cfg(test)]
mod tests {
- use super::anything;
use crate::prelude::*;
#[test]
diff --git a/crates/googletest/src/matchers/bool_matcher.rs b/crates/googletest/src/matchers/bool_matcher.rs
new file mode 100644
index 0000000..380e011
--- /dev/null
+++ b/crates/googletest/src/matchers/bool_matcher.rs
@@ -0,0 +1,99 @@
+// Copyright 2024 Google LLC
+//
+// 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.
+
+use crate::{
+ description::Description,
+ matcher::{Matcher, MatcherBase, MatcherResult},
+};
+
+/// Matches boolean value `true`.
+pub fn is_true() -> BoolMatcher {
+ BoolMatcher { expected: true }
+}
+
+/// Matches boolean value `false`.
+pub fn is_false() -> BoolMatcher {
+ BoolMatcher { expected: false }
+}
+
+/// Matches a bool value or bool reference.
+#[derive(MatcherBase)]
+pub struct BoolMatcher {
+ expected: bool,
+}
+
+impl BoolMatcher {
+ fn matches(&self, actual: bool) -> MatcherResult {
+ (actual == self.expected).into()
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ match (matcher_result, self.expected) {
+ (MatcherResult::Match, true) | (MatcherResult::NoMatch, false) => "is true".into(),
+ (MatcherResult::Match, false) | (MatcherResult::NoMatch, true) => "is false".into(),
+ }
+ }
+}
+
+impl Matcher<bool> for BoolMatcher {
+ fn matches(&self, actual: bool) -> MatcherResult {
+ self.matches(actual)
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ self.describe(matcher_result)
+ }
+}
+
+impl<'a> Matcher<&'a bool> for BoolMatcher {
+ fn matches(&self, actual: &'a bool) -> MatcherResult {
+ self.matches(*actual)
+ }
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ self.describe(matcher_result)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::prelude::*;
+
+ #[test]
+ fn match_value() -> Result<()> {
+ verify_that!(true, is_true())?;
+ verify_that!(true, not(is_false()))?;
+ verify_that!(false, is_false())?;
+ verify_that!(false, not(is_true()))
+ }
+
+ #[test]
+ fn match_ref() -> Result<()> {
+ let t = true;
+ let f = false;
+
+ verify_that!(&t, is_true())?;
+ verify_that!(&t, not(is_false()))?;
+ verify_that!(&f, is_false())?;
+ verify_that!(&f, not(is_true()))
+ }
+
+ #[test]
+ fn describe() {
+ assert_eq!(is_true().describe(MatcherResult::Match).to_string(), "is true");
+ assert_eq!(is_true().describe(MatcherResult::NoMatch).to_string(), "is false");
+ assert_eq!(is_false().describe(MatcherResult::Match).to_string(), "is false");
+ assert_eq!(is_false().describe(MatcherResult::NoMatch).to_string(), "is true");
+ }
+}
diff --git a/crates/googletest/src/matchers/char_count_matcher.rs b/crates/googletest/src/matchers/char_count_matcher.rs
index 70977d7..4f11edd 100644
--- a/crates/googletest/src/matchers/char_count_matcher.rs
+++ b/crates/googletest/src/matchers/char_count_matcher.rs
@@ -14,9 +14,9 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches a string whose number of Unicode scalars matches `expected`.
///
@@ -56,22 +56,18 @@
/// # }
/// # should_pass().unwrap();
/// ```
-pub fn char_count<T: Debug + ?Sized + AsRef<str>, E: Matcher<ActualT = usize>>(
- expected: E,
-) -> impl Matcher<ActualT = T> {
- CharLenMatcher { expected, phantom: Default::default() }
+pub fn char_count<E: Matcher<usize>>(expected: E) -> CharLenMatcher<E> {
+ CharLenMatcher { expected }
}
-struct CharLenMatcher<T: ?Sized, E> {
+#[derive(MatcherBase)]
+pub struct CharLenMatcher<E> {
expected: E,
- phantom: PhantomData<T>,
}
-impl<T: Debug + ?Sized + AsRef<str>, E: Matcher<ActualT = usize>> Matcher for CharLenMatcher<T, E> {
- type ActualT = T;
-
- fn matches(&self, actual: &T) -> MatcherResult {
- self.expected.matches(&actual.as_ref().chars().count())
+impl<T: Debug + Copy + AsRef<str>, E: Matcher<usize>> Matcher<T> for CharLenMatcher<E> {
+ fn matches(&self, actual: T) -> MatcherResult {
+ self.expected.matches(actual.as_ref().chars().count())
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -89,12 +85,12 @@
}
}
- fn explain_match(&self, actual: &T) -> Description {
+ fn explain_match(&self, actual: T) -> Description {
let actual_size = actual.as_ref().chars().count();
format!(
"which has character count {}, {}",
actual_size,
- self.expected.explain_match(&actual_size)
+ self.expected.explain_match(actual_size)
)
.into()
}
@@ -102,13 +98,11 @@
#[cfg(test)]
mod tests {
- use super::char_count;
use crate::description::Description;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
use std::fmt::Debug;
- use std::marker::PhantomData;
#[test]
fn char_count_matches_string_slice() -> Result<()> {
@@ -130,11 +124,11 @@
#[test]
fn char_count_explains_match() -> Result<()> {
- struct TestMatcher<T>(PhantomData<T>);
- impl<T: Debug> Matcher for TestMatcher<T> {
- type ActualT = T;
+ #[derive(MatcherBase)]
+ struct TestMatcher;
- fn matches(&self, _: &T) -> MatcherResult {
+ impl<T: Debug + Copy> Matcher<T> for TestMatcher {
+ fn matches(&self, _: T) -> MatcherResult {
false.into()
}
@@ -142,12 +136,12 @@
"called described".into()
}
- fn explain_match(&self, _: &T) -> Description {
+ fn explain_match(&self, _: T) -> Description {
"called explain_match".into()
}
}
verify_that!(
- char_count(TestMatcher(Default::default())).explain_match(&"A string"),
+ char_count(TestMatcher).explain_match("A string"),
displays_as(eq("which has character count 8, called explain_match"))
)
}
diff --git a/crates/googletest/src/matchers/conjunction_matcher.rs b/crates/googletest/src/matchers/conjunction_matcher.rs
index dc50833..b2e2e27 100644
--- a/crates/googletest/src/matchers/conjunction_matcher.rs
+++ b/crates/googletest/src/matchers/conjunction_matcher.rs
@@ -17,56 +17,87 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
-/// Matcher created by [`Matcher::and`].
+/// Matcher created by [`Matcher::and`] and [`all!`].
+///
+/// Both [`Matcher::and`] and [`all!`] nest on m1. In other words,
+/// both `x.and(y).and(z)` and `all![x, y, z]` produce:
+/// ```ignore
+/// ConjunctionMatcher {
+/// m1: ConjunctionMatcher {
+/// m1: x,
+/// m2: y
+/// },
+/// m2: z
+/// }
+/// ```
+///
+/// This behavior must be respected
+/// to ensure that [`Matcher::explain_match`] and [`Matcher::describe`] produce
+/// useful descriptions.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
+#[derive(MatcherBase)]
pub struct ConjunctionMatcher<M1, M2> {
m1: M1,
m2: M2,
}
impl<M1, M2> ConjunctionMatcher<M1, M2> {
- pub(crate) fn new(m1: M1, m2: M2) -> Self {
+ pub fn new(m1: M1, m2: M2) -> Self {
Self { m1, m2 }
}
}
-impl<M1: Matcher, M2: Matcher<ActualT = M1::ActualT>> Matcher for ConjunctionMatcher<M1, M2>
-where
- M1::ActualT: Debug,
-{
- type ActualT = M1::ActualT;
-
- fn matches(&self, actual: &M1::ActualT) -> MatcherResult {
+impl<T: Debug + Copy, M1: Matcher<T>, M2: Matcher<T>> Matcher<T> for ConjunctionMatcher<M1, M2> {
+ fn matches(&self, actual: T) -> MatcherResult {
match (self.m1.matches(actual), self.m2.matches(actual)) {
(MatcherResult::Match, MatcherResult::Match) => MatcherResult::Match,
_ => MatcherResult::NoMatch,
}
}
- fn explain_match(&self, actual: &M1::ActualT) -> Description {
+ fn explain_match(&self, actual: T) -> Description {
match (self.m1.matches(actual), self.m2.matches(actual)) {
- (MatcherResult::Match, MatcherResult::Match) => Description::new()
- .nested(self.m1.explain_match(actual))
- .text("and")
- .nested(self.m2.explain_match(actual)),
(MatcherResult::NoMatch, MatcherResult::Match) => self.m1.explain_match(actual),
(MatcherResult::Match, MatcherResult::NoMatch) => self.m2.explain_match(actual),
- (MatcherResult::NoMatch, MatcherResult::NoMatch) => Description::new()
- .nested(self.m1.explain_match(actual))
- .text("and")
- .nested(self.m2.explain_match(actual)),
+ (_, _) => {
+ let m1_description = self.m1.explain_match(actual);
+ if m1_description.is_conjunction_description() {
+ m1_description.nested(self.m2.explain_match(actual))
+ } else {
+ Description::new()
+ .bullet_list()
+ .collect([m1_description, self.m2.explain_match(actual)])
+ .conjunction_description()
+ }
+ }
}
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
- format!("{}, and {}", self.m1.describe(matcher_result), self.m2.describe(matcher_result))
- .into()
+ let m1_description = self.m1.describe(matcher_result);
+ if m1_description.is_conjunction_description() {
+ m1_description.push_in_last_nested(self.m2.describe(matcher_result))
+ } else {
+ let header = if matcher_result.into() {
+ "has all the following properties:"
+ } else {
+ "has at least one of the following properties:"
+ };
+ Description::new()
+ .text(header)
+ .nested(
+ Description::new()
+ .bullet_list()
+ .collect([m1_description, self.m2.describe(matcher_result)]),
+ )
+ .conjunction_description()
+ }
}
}
@@ -88,7 +119,9 @@
err(displays_as(contains_substring(indoc!(
"
Value of: 1
- Expected: is anything, and never matches
+ Expected: has all the following properties:
+ * is anything
+ * never matches
Actual: 1,
which is anything
"
@@ -104,7 +137,9 @@
err(displays_as(contains_substring(indoc!(
"
Value of: 1
- Expected: never matches, and is anything
+ Expected: has all the following properties:
+ * never matches
+ * is anything
Actual: 1,
which is anything
"
@@ -120,11 +155,37 @@
err(displays_as(contains_substring(indoc!(
"
Value of: 1
- Expected: never matches, and never matches
+ Expected: has all the following properties:
+ * never matches
+ * never matches
Actual: 1,
- which is anything
- and
- which is anything
+ * which is anything
+ * which is anything
+ "
+ ))))
+ )
+ }
+
+ #[test]
+ fn and_long_chain_of_matchers() -> Result<()> {
+ let result = verify_that!(
+ 1,
+ anything().and(not(anything())).and(anything()).and(not(anything())).and(anything())
+ );
+ verify_that!(
+ result,
+ err(displays_as(contains_substring(indoc!(
+ "
+ Value of: 1
+ Expected: has all the following properties:
+ * is anything
+ * never matches
+ * is anything
+ * never matches
+ * is anything
+ Actual: 1,
+ * which is anything
+ * which is anything
"
))))
)
@@ -132,7 +193,7 @@
#[test]
fn chained_and_matches() -> Result<()> {
- #[derive(Debug)]
+ #[derive(Debug, Clone, Copy)]
struct Struct {
a: i32,
b: i32,
diff --git a/crates/googletest/src/matchers/container_eq_matcher.rs b/crates/googletest/src/matchers/container_eq_matcher.rs
index d4f872c..7eb0933 100644
--- a/crates/googletest/src/matchers/container_eq_matcher.rs
+++ b/crates/googletest/src/matchers/container_eq_matcher.rs
@@ -13,9 +13,8 @@
// limitations under the License.
use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use std::fmt::Debug;
-use std::marker::PhantomData;
/// Matches a container equal (in the sense of `==`) to `expected`.
///
@@ -30,15 +29,12 @@
/// Unexpected: [4]
/// ```
///
-/// The actual value must be a container such as a `Vec`, an array, or a
-/// dereferenced slice. More precisely, a shared borrow of the actual value must
+/// The actual value must be a container such as a `&Vec`, an array, or a
+/// dereferenced slice. More precisely, the actual value must
/// implement [`IntoIterator`] whose `Item` type implements
/// [`PartialEq<ExpectedT>`], where `ExpectedT` is the element type of the
/// expected value.
///
-/// If the container type is a `Vec`, then the expected type may be a slice of
-/// the same element type. For example:
-///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
@@ -49,78 +45,40 @@
/// # should_pass().unwrap();
/// ```
///
-/// As an exception, if the actual type is a `Vec<String>`, the expected type
-/// may be a slice of `&str`:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # fn should_pass() -> Result<()> {
-/// let vec: Vec<String> = vec!["A string".into(), "Another string".into()];
-/// verify_that!(vec, container_eq(["A string", "Another string"]))?;
-/// # Ok(())
-/// # }
-/// # should_pass().unwrap();
-/// ```
-///
-/// These exceptions allow one to avoid unnecessary allocations in test
-/// assertions.
-///
-/// One can also check container equality of a slice with an array. To do so,
-/// dereference the slice:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # fn should_pass() -> Result<()> {
-/// let value = &[1, 2, 3];
-/// verify_that!(*value, container_eq([1, 2, 3]))?;
-/// # Ok(())
-/// # }
-/// # should_pass().unwrap();
-/// ```
-///
-/// Otherwise, the actual and expected types must be identical.
-///
/// *Performance note*: In the event of a mismatch leading to an assertion
/// failure, the construction of the lists of missing and unexpected values
/// uses a naive algorithm requiring time proportional to the product of the
/// sizes of the expected and actual values. This should therefore only be used
/// when the containers are small enough that this is not a problem.
-// This returns ContainerEqMatcher and not impl Matcher because
-// ContainerEqMatcher has some specialisations for slice types (see
-// documentation above). Returning impl Matcher would hide those from the
-// compiler.
-pub fn container_eq<ActualContainerT, ExpectedContainerT>(
+pub fn container_eq<ExpectedContainerT>(
expected: ExpectedContainerT,
-) -> ContainerEqMatcher<ActualContainerT, ExpectedContainerT>
+) -> ContainerEqMatcher<ExpectedContainerT>
where
- ActualContainerT: PartialEq<ExpectedContainerT> + Debug + ?Sized,
ExpectedContainerT: Debug,
{
- ContainerEqMatcher { expected, phantom: Default::default() }
+ ContainerEqMatcher { expected }
}
-pub struct ContainerEqMatcher<ActualContainerT: ?Sized, ExpectedContainerT> {
+#[derive(MatcherBase)]
+pub struct ContainerEqMatcher<ExpectedContainerT> {
expected: ExpectedContainerT,
- phantom: PhantomData<ActualContainerT>,
}
-impl<ActualElementT, ActualContainerT, ExpectedElementT, ExpectedContainerT> Matcher
- for ContainerEqMatcher<ActualContainerT, ExpectedContainerT>
+impl<ActualElementT, ActualContainerT, ExpectedElementT, ExpectedContainerT>
+ Matcher<ActualContainerT> for ContainerEqMatcher<ExpectedContainerT>
where
- ActualElementT: PartialEq<ExpectedElementT> + Debug + ?Sized,
- ActualContainerT: PartialEq<ExpectedContainerT> + Debug + ?Sized,
+ ActualElementT: for<'a> PartialEq<&'a ExpectedElementT> + Debug + Copy,
+ ActualContainerT: for<'a> PartialEq<&'a ExpectedContainerT> + Debug + Copy,
ExpectedElementT: Debug,
ExpectedContainerT: Debug,
- for<'a> &'a ActualContainerT: IntoIterator<Item = &'a ActualElementT>,
+ ActualContainerT: IntoIterator<Item = ActualElementT>,
for<'a> &'a ExpectedContainerT: IntoIterator<Item = &'a ExpectedElementT>,
{
- type ActualT = ActualContainerT;
-
- fn matches(&self, actual: &ActualContainerT) -> MatcherResult {
- (*actual == self.expected).into()
+ fn matches(&self, actual: ActualContainerT) -> MatcherResult {
+ (actual == &self.expected).into()
}
- fn explain_match(&self, actual: &ActualContainerT) -> Description {
+ fn explain_match(&self, actual: ActualContainerT) -> Description {
build_explanation(self.get_missing_items(actual), self.get_unexpected_items(actual)).into()
}
@@ -132,20 +90,32 @@
}
}
-impl<ActualElementT, ActualContainerT, ExpectedElementT, ExpectedContainerT>
- ContainerEqMatcher<ActualContainerT, ExpectedContainerT>
+impl<ExpectedElementT, ExpectedContainerT> ContainerEqMatcher<ExpectedContainerT>
where
- ActualElementT: PartialEq<ExpectedElementT> + ?Sized,
- ActualContainerT: PartialEq<ExpectedContainerT> + ?Sized,
- for<'a> &'a ActualContainerT: IntoIterator<Item = &'a ActualElementT>,
for<'a> &'a ExpectedContainerT: IntoIterator<Item = &'a ExpectedElementT>,
{
- fn get_missing_items(&self, actual: &ActualContainerT) -> Vec<&ExpectedElementT> {
- self.expected.into_iter().filter(|&i| !actual.into_iter().any(|j| j == i)).collect()
+ fn get_missing_items<ActualElementT, ActualContainerT>(
+ &self,
+ actual: ActualContainerT,
+ ) -> Vec<&'_ ExpectedElementT>
+ where
+ ActualElementT: for<'a> PartialEq<&'a ExpectedElementT> + Copy,
+ ActualContainerT: for<'a> PartialEq<&'a ExpectedContainerT> + Copy,
+ ActualContainerT: IntoIterator<Item = ActualElementT>,
+ {
+ self.expected.into_iter().filter(|i| !actual.into_iter().any(|j| j == *i)).collect()
}
- fn get_unexpected_items<'a>(&self, actual: &'a ActualContainerT) -> Vec<&'a ActualElementT> {
- actual.into_iter().filter(|&i| !self.expected.into_iter().any(|j| i == j)).collect()
+ fn get_unexpected_items<ActualElementT, ActualContainerT>(
+ &self,
+ actual: ActualContainerT,
+ ) -> Vec<ActualElementT>
+ where
+ ActualElementT: for<'a> PartialEq<&'a ExpectedElementT> + Copy,
+ ActualContainerT: for<'a> PartialEq<&'a ExpectedContainerT> + Copy,
+ ActualContainerT: IntoIterator<Item = ActualElementT>,
+ {
+ actual.into_iter().filter(|i| !self.expected.into_iter().any(|j| i == &j)).collect()
}
}
@@ -185,8 +155,7 @@
#[cfg(test)]
mod tests {
- use super::container_eq;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
use std::collections::HashSet;
@@ -199,7 +168,7 @@
#[test]
fn container_eq_matches_array_with_slice() -> Result<()> {
let value = &[1, 2, 3];
- verify_that!(*value, container_eq([1, 2, 3]))
+ verify_that!(value, container_eq([1, 2, 3]))
}
#[test]
@@ -271,15 +240,15 @@
}
#[test]
- fn container_eq_matches_owned_vec_of_owned_strings_with_slice_of_string_references()
- -> Result<()> {
+ fn container_eq_matches_owned_vec_of_owned_strings_with_slice_of_string_references(
+ ) -> Result<()> {
let vector = vec!["A string".to_string(), "Another string".to_string()];
verify_that!(vector, container_eq(["A string", "Another string"]))
}
#[test]
- fn container_eq_matches_owned_vec_of_owned_strings_with_shorter_slice_of_string_references()
- -> Result<()> {
+ fn container_eq_matches_owned_vec_of_owned_strings_with_shorter_slice_of_string_references(
+ ) -> Result<()> {
let actual = vec!["A string".to_string(), "Another string".to_string()];
let matcher = container_eq(["A string"]);
diff --git a/crates/googletest/src/matchers/contains_matcher.rs b/crates/googletest/src/matchers/contains_matcher.rs
index 1a27ce0..3ce60ec 100644
--- a/crates/googletest/src/matchers/contains_matcher.rs
+++ b/crates/googletest/src/matchers/contains_matcher.rs
@@ -14,11 +14,12 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
-/// Matches an iterable type whose elements contain a value matched by `inner`.
+/// Matches an [`IntoIterator`] type whose elements contain a value matched by
+/// `inner`.
///
/// By default, this matches a container with any number of elements matched
/// by `inner`. Use the method [`ContainsMatcher::times`] to constrain the
@@ -28,11 +29,11 @@
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// verify_that!(["Some value"], contains(eq("Some value")))?; // Passes
-/// verify_that!(vec!["Some value"], contains(eq("Some value")))?; // Passes
+/// verify_that!(vec!["Some value"], contains(eq(&"Some value")))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
-/// verify_that!([] as [String; 0], contains(eq("Some value")))?; // Fails
+/// verify_that!([] as [&String; 0], contains(eq("Some value")))?; // Fails
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
@@ -43,19 +44,19 @@
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// ```
-pub fn contains<T, InnerMatcherT>(inner: InnerMatcherT) -> ContainsMatcher<T, InnerMatcherT> {
- ContainsMatcher { inner, count: None, phantom: Default::default() }
+pub fn contains<InnerMatcherT>(inner: InnerMatcherT) -> ContainsMatcher<InnerMatcherT> {
+ ContainsMatcher { inner, count: None }
}
/// A matcher which matches a container containing one or more elements a given
/// inner [`Matcher`] matches.
-pub struct ContainsMatcher<T, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct ContainsMatcher<InnerMatcherT> {
inner: InnerMatcherT,
- count: Option<Box<dyn Matcher<ActualT = usize>>>,
- phantom: PhantomData<T>,
+ count: Option<Box<dyn Matcher<usize>>>,
}
-impl<T, InnerMatcherT> ContainsMatcher<T, InnerMatcherT> {
+impl<InnerMatcherT> ContainsMatcher<InnerMatcherT> {
/// Configures this instance to match containers which contain a number of
/// matching items matched by `count`.
///
@@ -68,7 +69,7 @@
///
/// One can also use `times(eq(0))` to test for the *absence* of an item
/// matching the expected value.
- pub fn times(mut self, count: impl Matcher<ActualT = usize> + 'static) -> Self {
+ pub fn times(mut self, count: impl Matcher<usize> + 'static) -> Self {
self.count = Some(Box::new(count));
self
}
@@ -84,16 +85,14 @@
// because val is dropped before matcher but the trait bound requires that
// the argument to matches outlive the matcher. It works fine if one defines
// val before matcher.
-impl<T: Debug, InnerMatcherT: Matcher<ActualT = T>, ContainerT: Debug> Matcher
- for ContainsMatcher<ContainerT, InnerMatcherT>
+impl<T: Debug + Copy, InnerMatcherT: Matcher<T>, ContainerT: Debug + Copy> Matcher<ContainerT>
+ for ContainsMatcher<InnerMatcherT>
where
- for<'a> &'a ContainerT: IntoIterator<Item = &'a T>,
+ ContainerT: IntoIterator<Item = T>,
{
- type ActualT = ContainerT;
-
- fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+ fn matches(&self, actual: ContainerT) -> MatcherResult {
if let Some(count) = &self.count {
- count.matches(&self.count_matches(actual))
+ count.matches(self.count_matches(actual))
} else {
for v in actual.into_iter() {
if self.inner.matches(v).into() {
@@ -104,7 +103,7 @@
}
}
- fn explain_match(&self, actual: &Self::ActualT) -> Description {
+ fn explain_match(&self, actual: ContainerT) -> Description {
let count = self.count_matches(actual);
match (count, &self.count) {
(_, Some(_)) => format!("which contains {} matching elements", count).into(),
@@ -140,11 +139,11 @@
}
}
-impl<ActualT, InnerMatcherT> ContainsMatcher<ActualT, InnerMatcherT> {
- fn count_matches<T: Debug, ContainerT>(&self, actual: &ContainerT) -> usize
+impl<InnerMatcherT> ContainsMatcher<InnerMatcherT> {
+ fn count_matches<T: Debug + Copy, ContainerT>(&self, actual: ContainerT) -> usize
where
- for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
- InnerMatcherT: Matcher<ActualT = T>,
+ ContainerT: IntoIterator<Item = T>,
+ InnerMatcherT: Matcher<T>,
{
let mut count = 0;
for v in actual.into_iter() {
@@ -158,13 +157,12 @@
#[cfg(test)]
mod tests {
- use super::{contains, ContainsMatcher};
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
#[test]
fn contains_matches_singleton_slice_with_value() -> Result<()> {
- let matcher = contains(eq(1));
+ let matcher = contains(eq(&1));
let result = matcher.matches(&vec![1]);
@@ -173,7 +171,7 @@
#[test]
fn contains_matches_singleton_vec_with_value() -> Result<()> {
- let matcher = contains(eq(1));
+ let matcher = contains(eq(&1));
let result = matcher.matches(&vec![1]);
@@ -182,7 +180,7 @@
#[test]
fn contains_matches_two_element_slice_with_value() -> Result<()> {
- let matcher = contains(eq(1));
+ let matcher = contains(eq(&1));
let result = matcher.matches(&[0, 1]);
@@ -191,7 +189,7 @@
#[test]
fn contains_does_not_match_singleton_slice_with_wrong_value() -> Result<()> {
- let matcher = contains(eq(1));
+ let matcher = contains(eq(&1));
let result = matcher.matches(&[0]);
@@ -200,16 +198,16 @@
#[test]
fn contains_does_not_match_empty_slice() -> Result<()> {
- let matcher = contains(eq::<i32, _>(1));
+ let matcher = contains(eq(&1));
- let result = matcher.matches(&[]);
+ let result = matcher.matches(&[1; 0]);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn contains_matches_slice_with_repeated_value() -> Result<()> {
- let matcher = contains(eq(1)).times(eq(2));
+ let matcher = contains(eq(&1)).times(eq(2));
let result = matcher.matches(&[1, 1]);
@@ -218,7 +216,7 @@
#[test]
fn contains_does_not_match_slice_with_too_few_of_value() -> Result<()> {
- let matcher = contains(eq(1)).times(eq(2));
+ let matcher = contains(eq(&1)).times(eq(2));
let result = matcher.matches(&[0, 1]);
@@ -227,7 +225,7 @@
#[test]
fn contains_does_not_match_slice_with_too_many_of_value() -> Result<()> {
- let matcher = contains(eq(1)).times(eq(1));
+ let matcher = contains(eq(&1)).times(eq(1));
let result = matcher.matches(&[1, 1]);
@@ -236,20 +234,20 @@
#[test]
fn contains_formats_without_multiplicity_by_default() -> Result<()> {
- let matcher: ContainsMatcher<Vec<i32>, _> = contains(eq(1));
+ let matcher = contains(eq(&1));
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&Vec<i32>>::describe(&matcher, MatcherResult::Match),
displays_as(eq("contains at least one element which is equal to 1"))
)
}
#[test]
fn contains_formats_with_multiplicity_when_specified() -> Result<()> {
- let matcher: ContainsMatcher<Vec<i32>, _> = contains(eq(1)).times(eq(2));
+ let matcher = contains(eq(&1)).times(eq(2));
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&Vec<i32>>::describe(&matcher, MatcherResult::Match),
displays_as(eq("contains n elements which is equal to 1\n where n is equal to 2"))
)
}
@@ -257,7 +255,7 @@
#[test]
fn contains_mismatch_shows_number_of_times_element_was_found() -> Result<()> {
verify_that!(
- contains(eq(3)).times(eq(1)).explain_match(&vec![1, 2, 3, 3]),
+ contains(eq(&3)).times(eq(1)).explain_match(&vec![1, 2, 3, 3]),
displays_as(eq("which contains 2 matching elements"))
)
}
@@ -265,7 +263,7 @@
#[test]
fn contains_mismatch_shows_when_matches() -> Result<()> {
verify_that!(
- contains(eq(3)).explain_match(&vec![1, 2, 3, 3]),
+ contains(eq(&3)).explain_match(&vec![1, 2, 3, 3]),
displays_as(eq("which contains a matching element"))
)
}
@@ -273,7 +271,7 @@
#[test]
fn contains_mismatch_shows_when_no_matches() -> Result<()> {
verify_that!(
- contains(eq(3)).explain_match(&vec![1, 2]),
+ contains(eq(&3)).explain_match(&vec![1, 2]),
displays_as(eq("which does not contain a matching element"))
)
}
diff --git a/crates/googletest/src/matchers/contains_regex_matcher.rs b/crates/googletest/src/matchers/contains_regex_matcher.rs
index 6f68168..61a2d30 100644
--- a/crates/googletest/src/matchers/contains_regex_matcher.rs
+++ b/crates/googletest/src/matchers/contains_regex_matcher.rs
@@ -13,10 +13,9 @@
// limitations under the License.
use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use regex::Regex;
use std::fmt::Debug;
-use std::marker::PhantomData;
use std::ops::Deref;
/// Matches a string containing a substring which matches the given regular
@@ -47,18 +46,9 @@
///
/// Panics if the given `pattern` is not a syntactically valid regular
/// expression.
-// N.B. This returns the concrete type rather than an impl Matcher so that it
-// can act simultaneously as a Matcher<str> and a Matcher<String>. Otherwise the
-// compiler treats it as a Matcher<str> only and the code
-// verify_that!("Some value".to_string(), contains_regex(".*value"))?;
-// doesn't compile.
-pub fn contains_regex<ActualT: ?Sized, PatternT: Deref<Target = str>>(
- pattern: PatternT,
-) -> ContainsRegexMatcher<ActualT> {
- ContainsRegexMatcher {
- regex: Regex::new(pattern.deref()).unwrap(),
- phantom: Default::default(),
- }
+#[track_caller]
+pub fn contains_regex<PatternT: Deref<Target = str>>(pattern: PatternT) -> ContainsRegexMatcher {
+ ContainsRegexMatcher { regex: Regex::new(pattern.deref()).unwrap() }
}
/// A matcher matching a string-like type containing a substring matching a
@@ -66,15 +56,13 @@
///
/// Intended only to be used from the function [`contains_regex`] only.
/// Should not be referenced by code outside this library.
-pub struct ContainsRegexMatcher<ActualT: ?Sized> {
+#[derive(MatcherBase)]
+pub struct ContainsRegexMatcher {
regex: Regex,
- phantom: PhantomData<ActualT>,
}
-impl<ActualT: AsRef<str> + Debug + ?Sized> Matcher for ContainsRegexMatcher<ActualT> {
- type ActualT = ActualT;
-
- fn matches(&self, actual: &ActualT) -> MatcherResult {
+impl<ActualT: AsRef<str> + Debug + Copy> Matcher<ActualT> for ContainsRegexMatcher {
+ fn matches(&self, actual: ActualT) -> MatcherResult {
self.regex.is_match(actual.as_ref()).into()
}
@@ -92,8 +80,7 @@
#[cfg(test)]
mod tests {
- use super::{contains_regex, ContainsRegexMatcher};
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
#[test]
@@ -139,10 +126,10 @@
#[test]
fn contains_regex_displays_quoted_debug_of_pattern() -> Result<()> {
- let matcher: ContainsRegexMatcher<&str> = contains_regex("\n");
+ let matcher = contains_regex("\n");
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("contains the regular expression \"\\n\""))
)
}
diff --git a/crates/googletest/src/matchers/derefs_to_matcher.rs b/crates/googletest/src/matchers/derefs_to_matcher.rs
new file mode 100644
index 0000000..2c9082e
--- /dev/null
+++ b/crates/googletest/src/matchers/derefs_to_matcher.rs
@@ -0,0 +1,99 @@
+// Copyright 2024 Google LLC
+//
+// 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.
+
+use crate::{
+ description::Description,
+ matcher::{Matcher, MatcherBase, MatcherResult},
+};
+use std::{fmt::Debug, ops::Deref};
+
+/// Dereferences the `actual` value and verifies that the returned reference
+/// matches the `inner` matcher.
+///
+/// ```
+/// # use googletest::{matchers::{derefs_to, eq}, verify_that};
+/// verify_that!(Box::new(123), derefs_to(eq(&123)))
+/// # .unwrap()
+/// ```
+pub fn derefs_to<Inner>(inner: Inner) -> DerefsTo<Inner> {
+ DerefsTo { inner }
+}
+
+/// A matcher which derefs a value and verifies that the result matches the
+/// `inner` matcher.
+///
+/// See [`derefs_to`].
+#[derive(MatcherBase)]
+pub struct DerefsTo<InnerT> {
+ pub(crate) inner: InnerT,
+}
+
+impl<'a, ActualT, ExpectedT, Inner> Matcher<&'a ActualT> for DerefsTo<Inner>
+where
+ ActualT: Deref<Target = ExpectedT> + Debug,
+ ExpectedT: Copy + Debug + 'a,
+ Inner: Matcher<&'a ExpectedT>,
+{
+ fn matches(&self, actual: &'a ActualT) -> MatcherResult {
+ self.inner.matches(actual.deref())
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ self.inner.describe(matcher_result)
+ }
+
+ fn explain_match(&self, actual: &'a ActualT) -> Description {
+ self.inner.explain_match(actual.deref())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::rc::Rc;
+
+ use crate::prelude::*;
+ use indoc::indoc;
+
+ #[test]
+ fn deref_to_matches_box_of_int_with_int() -> Result<()> {
+ let actual = Box::new(123);
+ verify_that!(actual, derefs_to(eq(&123)))
+ }
+
+ #[test]
+ fn deref_to_matches_rc_of_int_with_int() -> Result<()> {
+ verify_that!(Rc::new(123), derefs_to(eq(&123)))
+ }
+
+ #[test]
+ fn deref_to_combines_with_points_to_for_copy() -> Result<()> {
+ verify_that!(Rc::new(123), derefs_to(points_to(eq(123))))
+ }
+
+ #[test]
+ fn match_explanation_references_actual_value() -> Result<()> {
+ let actual = Box::new(1);
+ let result = verify_that!(actual, derefs_to(eq(&0)));
+
+ verify_that!(
+ result,
+ err(displays_as(contains_substring(indoc!(
+ "
+ Actual: 1,
+ which isn't equal to 0
+ "
+ ))))
+ )
+ }
+}
diff --git a/crates/googletest/src/matchers/disjunction_matcher.rs b/crates/googletest/src/matchers/disjunction_matcher.rs
index dd56be2..77e18f6 100644
--- a/crates/googletest/src/matchers/disjunction_matcher.rs
+++ b/crates/googletest/src/matchers/disjunction_matcher.rs
@@ -17,48 +17,81 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
-/// Matcher created by [`Matcher::or`].
+/// Matcher created by [`Matcher::or`] and [`any!`].
///
+/// Both [`Matcher::or`] and [`any!`] nest on m1. In other words,
+/// both `x.or(y).or(z)` and `any![x, y, z]` produce:
+/// ```ignore
+/// DisjunctionMatcher {
+/// m1: DisjunctionMatcher {
+/// m1: x, m2: y
+/// },
+/// m2: z
+/// }
+/// ```
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
+#[derive(MatcherBase)]
pub struct DisjunctionMatcher<M1, M2> {
m1: M1,
m2: M2,
}
impl<M1, M2> DisjunctionMatcher<M1, M2> {
- pub(crate) fn new(m1: M1, m2: M2) -> Self {
+ pub fn new(m1: M1, m2: M2) -> Self {
Self { m1, m2 }
}
}
-impl<M1: Matcher, M2: Matcher<ActualT = M1::ActualT>> Matcher for DisjunctionMatcher<M1, M2>
-where
- M1::ActualT: Debug,
-{
- type ActualT = M1::ActualT;
-
- fn matches(&self, actual: &M1::ActualT) -> MatcherResult {
+impl<T: Debug + Copy, M1: Matcher<T>, M2: Matcher<T>> Matcher<T> for DisjunctionMatcher<M1, M2> {
+ fn matches(&self, actual: T) -> MatcherResult {
match (self.m1.matches(actual), self.m2.matches(actual)) {
(MatcherResult::NoMatch, MatcherResult::NoMatch) => MatcherResult::NoMatch,
_ => MatcherResult::Match,
}
}
- fn explain_match(&self, actual: &M1::ActualT) -> Description {
- Description::new()
- .nested(self.m1.explain_match(actual))
- .text("and")
- .nested(self.m2.explain_match(actual))
+ fn explain_match(&self, actual: T) -> Description {
+ match (self.m1.matches(actual), self.m2.matches(actual)) {
+ (MatcherResult::NoMatch, MatcherResult::Match) => self.m1.explain_match(actual),
+ (MatcherResult::Match, MatcherResult::NoMatch) => self.m2.explain_match(actual),
+ (_, _) => {
+ let m1_description = self.m1.explain_match(actual);
+ if m1_description.is_disjunction_description() {
+ m1_description.nested(self.m2.explain_match(actual))
+ } else {
+ Description::new()
+ .bullet_list()
+ .collect([m1_description, self.m2.explain_match(actual)])
+ .disjunction_description()
+ }
+ }
+ }
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
- format!("{}, or {}", self.m1.describe(matcher_result), self.m2.describe(matcher_result))
- .into()
+ let m1_description = self.m1.describe(matcher_result);
+ if m1_description.is_disjunction_description() {
+ m1_description.push_in_last_nested(self.m2.describe(matcher_result))
+ } else {
+ let header = if matcher_result.into() {
+ "has at least one of the following properties:"
+ } else {
+ "has all of the following properties:"
+ };
+ Description::new()
+ .text(header)
+ .nested(
+ Description::new()
+ .bullet_list()
+ .collect([m1_description, self.m2.describe(matcher_result)]),
+ )
+ .disjunction_description()
+ }
}
}
@@ -90,11 +123,12 @@
err(displays_as(contains_substring(indoc!(
"
Value of: 1
- Expected: never matches, or never matches
+ Expected: has at least one of the following properties:
+ * never matches
+ * never matches
Actual: 1,
- which is anything
- and
- which is anything
+ * which is anything
+ * which is anything
"
))))
)
diff --git a/crates/googletest/src/matchers/display_matcher.rs b/crates/googletest/src/matchers/display_matcher.rs
index 51a5bff..9edccca 100644
--- a/crates/googletest/src/matchers/display_matcher.rs
+++ b/crates/googletest/src/matchers/display_matcher.rs
@@ -13,9 +13,8 @@
// limitations under the License.
use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use std::fmt::{Debug, Display};
-use std::marker::PhantomData;
/// Matches the string representation of types that implement `Display`.
///
@@ -23,29 +22,31 @@
/// let result: impl Display = ...;
/// verify_that!(result, displays_as(eq(format!("{}", result))))?;
/// ```
-pub fn displays_as<T: Debug + Display, InnerMatcher: Matcher<ActualT = String>>(
+pub fn displays_as<InnerMatcher: for<'a> Matcher<&'a str>>(
inner: InnerMatcher,
-) -> impl Matcher<ActualT = T> {
- DisplayMatcher::<T, _> { inner, phantom: Default::default() }
+) -> DisplayMatcher<InnerMatcher> {
+ DisplayMatcher { inner }
}
-struct DisplayMatcher<T, InnerMatcher: Matcher> {
+#[derive(MatcherBase)]
+pub struct DisplayMatcher<InnerMatcher> {
inner: InnerMatcher,
- phantom: PhantomData<T>,
}
-impl<T: Debug + Display, InnerMatcher: Matcher<ActualT = String>> Matcher
- for DisplayMatcher<T, InnerMatcher>
+impl<T: Debug + Display + Copy, InnerMatcher: for<'a> Matcher<&'a str>> Matcher<T>
+ for DisplayMatcher<InnerMatcher>
{
- type ActualT = T;
-
- fn matches(&self, actual: &T) -> MatcherResult {
+ fn matches(&self, actual: T) -> MatcherResult {
self.inner.matches(&format!("{actual}"))
}
- fn explain_match(&self, actual: &T) -> Description {
- format!("which displays as a string {}", self.inner.explain_match(&format!("{actual}")))
- .into()
+ fn explain_match(&self, actual: T) -> Description {
+ format!(
+ "which displays as {:?} {}",
+ actual.to_string(),
+ self.inner.explain_match(&format!("{actual}"))
+ )
+ .into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -65,7 +66,6 @@
#[cfg(test)]
mod tests {
- use super::displays_as;
use crate::prelude::*;
use indoc::indoc;
use std::fmt::{Debug, Display, Error, Formatter};
@@ -110,7 +110,8 @@
err(displays_as(contains_substring(indoc!(
"
Actual: \"123\\n234\",
- which displays as a string which isn't equal to \"123\\n345\"
+ which displays as \"123\\n234\" which isn't equal to \"123\\n345\"
+
Difference(-actual / +expected):
123
-234
diff --git a/crates/googletest/src/matchers/each_matcher.rs b/crates/googletest/src/matchers/each_matcher.rs
index 08a0742..6b76432 100644
--- a/crates/googletest/src/matchers/each_matcher.rs
+++ b/crates/googletest/src/matchers/each_matcher.rs
@@ -13,77 +13,58 @@
// limitations under the License.
use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
-use std::{fmt::Debug, marker::PhantomData};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
+use std::fmt::Debug;
/// Matches a container all of whose elements are matched by the matcher
/// `inner`.
///
-/// `T` can be any container such that `&T` implements `IntoIterator`. This
-/// includes `Vec`, arrays, and (dereferenced) slices.
+/// `T` must implement [`IntoIterator`]. This
+/// includes `&Vec`, arrays, and slices.
///
/// ```
/// # use googletest::prelude::*;
/// # use std::collections::HashSet;
/// # fn should_pass_1() -> Result<()> {
/// let value = vec![1, 2, 3];
-/// verify_that!(value, each(gt(0)))?; // Passes
+/// verify_that!(value, each(gt(&0)))?; // Passes
/// let array_value = [1, 2, 3];
/// verify_that!(array_value, each(gt(0)))?; // Passes
/// let slice_value = &[1, 2, 3];
-/// verify_that!(*slice_value, each(gt(0)))?; // Passes
+/// verify_that!(slice_value, each(gt(&0)))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// # let value = vec![1, 2, 3];
-/// verify_that!(value, each(lt(2)))?; // Fails: 2 and 3 are not less than 2
+/// verify_that!(value, each(lt(&2)))?; // Fails: 2 and 3 are not less than 2
/// # Ok(())
/// # }
///
/// # fn should_pass_2() -> Result<()> {
/// let value: HashSet<i32> = [1, 2, 3].into();
-/// verify_that!(value, each(gt(0)))?; // Passes
+/// verify_that!(value, each(gt(&0)))?; // Passes
/// # Ok(())
/// # }
/// # should_pass_1().unwrap();
/// # should_fail().unwrap_err();
/// # should_pass_2().unwrap();
/// ```
-///
-/// One can also verify the contents of a slice by dereferencing it:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # fn should_pass() -> Result<()> {
-/// let value = &[1, 2, 3];
-/// verify_that!(*value, each(gt(0)))?;
-/// # Ok(())
-/// # }
-/// # should_pass().unwrap();
-/// ```
-pub fn each<ElementT: Debug, ActualT: Debug + ?Sized, MatcherT>(
- inner: MatcherT,
-) -> impl Matcher<ActualT = ActualT>
-where
- for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
- MatcherT: Matcher<ActualT = ElementT>,
-{
- EachMatcher { inner, phantom: Default::default() }
+pub fn each<MatcherT>(inner: MatcherT) -> EachMatcher<MatcherT> {
+ EachMatcher { inner }
}
-struct EachMatcher<ActualT: ?Sized, MatcherT> {
+#[derive(MatcherBase)]
+pub struct EachMatcher<MatcherT> {
inner: MatcherT,
- phantom: PhantomData<ActualT>,
}
-impl<ElementT: Debug, ActualT: Debug + ?Sized, MatcherT> Matcher for EachMatcher<ActualT, MatcherT>
+impl<ElementT: Debug + Copy, ActualT: Debug + Copy, MatcherT> Matcher<ActualT>
+ for EachMatcher<MatcherT>
where
- for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
- MatcherT: Matcher<ActualT = ElementT>,
+ ActualT: IntoIterator<Item = ElementT>,
+ MatcherT: Matcher<ElementT>,
{
- type ActualT = ActualT;
-
- fn matches(&self, actual: &ActualT) -> MatcherResult {
+ fn matches(&self, actual: ActualT) -> MatcherResult {
for element in actual {
if self.inner.matches(element).is_no_match() {
return MatcherResult::NoMatch;
@@ -92,7 +73,7 @@
MatcherResult::Match
}
- fn explain_match(&self, actual: &ActualT) -> Description {
+ fn explain_match(&self, actual: ActualT) -> Description {
let mut non_matching_elements = Vec::new();
for (index, element) in actual.into_iter().enumerate() {
if self.inner.matches(element).is_no_match() {
@@ -137,7 +118,6 @@
#[cfg(test)]
mod tests {
- use super::each;
use crate::prelude::*;
use indoc::indoc;
use std::collections::HashSet;
@@ -145,19 +125,19 @@
#[test]
fn each_matches_empty_vec() -> Result<()> {
let value: Vec<i32> = vec![];
- verify_that!(value, each(gt(0)))
+ verify_that!(value, each(gt(&0)))
}
#[test]
fn each_matches_vec_with_one_element() -> Result<()> {
let value = vec![1];
- verify_that!(value, each(gt(0)))
+ verify_that!(value, each(gt(&0)))
}
#[test]
fn each_matches_vec_with_two_elements() -> Result<()> {
let value = vec![1, 2];
- verify_that!(value, each(gt(0)))
+ verify_that!(value, each(gt(&0)))
}
#[test]
@@ -169,24 +149,24 @@
#[test]
fn each_matches_hash_set_with_one_element() -> Result<()> {
let value: HashSet<i32> = [1].into();
- verify_that!(value, each(gt(0)))
+ verify_that!(value, each(gt(&0)))
}
#[test]
fn each_does_not_match_when_first_element_does_not_match() -> Result<()> {
let value = vec![0];
- verify_that!(value, not(each(gt(1))))
+ verify_that!(value, not(each(gt(&1))))
}
#[test]
fn each_does_not_match_when_second_element_does_not_match() -> Result<()> {
let value = vec![2, 0];
- verify_that!(value, not(each(gt(1))))
+ verify_that!(value, not(each(gt(&1))))
}
#[test]
fn each_shows_correct_message_when_first_item_does_not_match() -> Result<()> {
- let result = verify_that!(vec![0, 2, 3], each(gt(0)));
+ let result = verify_that!(vec![0, 2, 3], each(gt(&0)));
verify_that!(
result,
@@ -202,7 +182,7 @@
#[test]
fn each_shows_correct_message_when_second_item_does_not_match() -> Result<()> {
- let result = verify_that!(vec![1, 0, 3], each(gt(0)));
+ let result = verify_that!(vec![1, 0, 3], each(gt(&0)));
verify_that!(
result,
@@ -218,7 +198,7 @@
#[test]
fn each_shows_correct_message_when_first_two_items_do_not_match() -> Result<()> {
- let result = verify_that!(vec![0, 1, 3], each(gt(1)));
+ let result = verify_that!(vec![0, 1, 3], each(gt(&1)));
verify_that!(
result,
@@ -235,7 +215,7 @@
}
#[test]
fn each_shows_inner_explanation() -> Result<()> {
- let result = verify_that!(vec![vec![1, 2], vec![1]], each(each(eq(1))));
+ let result = verify_that!(vec![vec![1, 2], vec![1]], each(each(eq(&1))));
verify_that!(
result,
diff --git a/crates/googletest/src/matchers/elements_are_matcher.rs b/crates/googletest/src/matchers/elements_are_matcher.rs
index 3a4b5b2..a1b926b 100644
--- a/crates/googletest/src/matchers/elements_are_matcher.rs
+++ b/crates/googletest/src/matchers/elements_are_matcher.rs
@@ -24,19 +24,18 @@
///
/// ```
/// # use googletest::prelude::*;
-/// verify_that!(vec![1, 2, 3], elements_are![eq(1), anything(), gt(0).and(lt(123))])
+/// verify_that!(vec![1, 2, 3], elements_are![eq(&1), anything(), gt(&0).and(lt(&123))])
/// # .unwrap();
/// ```
///
-/// The actual value must be a container such as a `Vec`, an array, or a
-/// dereferenced slice. More precisely, a shared borrow of the actual value must
-/// implement [`IntoIterator`].
+/// The actual value must be a container such as a `&Vec`, an array, or a slice.
+/// More precisely, the actual value must implement [`IntoIterator`].
///
/// ```
/// # use googletest::prelude::*;
/// let vector = vec![1, 2, 3];
/// let slice = vector.as_slice();
-/// verify_that!(*slice, elements_are![eq(1), anything(), gt(0).and(lt(123))])
+/// verify_that!(slice, elements_are![eq(&1), anything(), gt(&0).and(lt(&123))])
/// # .unwrap();
/// ```
///
@@ -45,7 +44,7 @@
///
/// ```
/// # use googletest::prelude::*;
-/// verify_that!(vec![1, 2], [eq(1), eq(2)])
+/// verify_that!(vec![1, 2], [eq(&1), eq(&2)])
/// # .unwrap();
/// ```
///
@@ -55,19 +54,25 @@
///
/// ```compile_fail
/// # use googletest::prelude::*;
-/// verify_that!(vec![vec![1,2], vec![3]], [[eq(1), eq(2)], [eq(3)]])
+/// verify_that!(vec![vec![1,2], vec![3]], [[eq(&1), eq(&2)], [eq(&3)]])
/// # .unwrap();
/// ```
///
/// Use this instead:
/// ```
/// # use googletest::prelude::*;
-/// verify_that!(vec![vec![1,2], vec![3]], [elements_are![eq(1), eq(2)], elements_are![eq(3)]])
+/// verify_that!(vec![vec![1,2], vec![3]], [elements_are![eq(&1), eq(&2)], elements_are![eq(&3)]])
/// # .unwrap();
/// ```
///
-/// This matcher does not support matching directly against an [`Iterator`]. To
-/// match against an iterator, use [`Iterator::collect`] to build a [`Vec`].
+/// If an inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+///
+/// verify_that!(vec![1,2,3], elements_are![&1, lt(&1000), gt(&1)])
+/// # .unwrap();
+/// ```
///
/// Do not use this with unordered containers, since that will lead to flaky
/// tests. Use
@@ -82,8 +87,12 @@
#[doc(hidden)]
macro_rules! __elements_are {
($($matcher:expr),* $(,)?) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::ElementsAre;
- ElementsAre::new(vec![$(Box::new($matcher)),*])
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::ElementsAre::new(
+ vec![$(Box::new(
+ $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
+ $matcher
+ )
+ )),*])
}}
}
@@ -93,36 +102,34 @@
#[doc(hidden)]
pub mod internal {
use crate::description::Description;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use crate::matcher_support::zipped_iterator::zip;
- use std::{fmt::Debug, marker::PhantomData};
+ use std::fmt::Debug;
/// This struct is meant to be used only by the macro `elements_are!`.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
- pub struct ElementsAre<'a, ContainerT: ?Sized, T: Debug> {
- elements: Vec<Box<dyn Matcher<ActualT = T> + 'a>>,
- phantom: PhantomData<ContainerT>,
+ #[derive(MatcherBase)]
+ pub struct ElementsAre<'a, T: Debug + Copy> {
+ elements: Vec<Box<dyn Matcher<T> + 'a>>,
}
- impl<'a, ContainerT: ?Sized, T: Debug> ElementsAre<'a, ContainerT, T> {
+ impl<'a, T: Debug + Copy> ElementsAre<'a, T> {
/// Factory only intended for use in the macro `elements_are!`.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
- pub fn new(elements: Vec<Box<dyn Matcher<ActualT = T> + 'a>>) -> Self {
- Self { elements, phantom: Default::default() }
+ pub fn new(elements: Vec<Box<dyn Matcher<T> + 'a>>) -> Self {
+ Self { elements }
}
}
- impl<'a, T: Debug, ContainerT: Debug + ?Sized> Matcher for ElementsAre<'a, ContainerT, T>
+ impl<'a, T: Debug + Copy, ContainerT: Debug + Copy> Matcher<ContainerT> for ElementsAre<'a, T>
where
- for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
+ ContainerT: IntoIterator<Item = T>,
{
- type ActualT = ContainerT;
-
- fn matches(&self, actual: &ContainerT) -> MatcherResult {
+ fn matches(&self, actual: ContainerT) -> MatcherResult {
let mut zipped_iterator = zip(actual.into_iter(), self.elements.iter());
for (a, e) in zipped_iterator.by_ref() {
if e.matches(a).is_no_match() {
@@ -136,7 +143,7 @@
}
}
- fn explain_match(&self, actual: &ContainerT) -> Description {
+ fn explain_match(&self, actual: ContainerT) -> Description {
let actual_iterator = actual.into_iter();
let mut zipped_iterator = zip(actual_iterator, self.elements.iter());
let mut mismatches = Vec::new();
diff --git a/crates/googletest/src/matchers/empty_matcher.rs b/crates/googletest/src/matchers/empty_matcher.rs
index 11cb675..7631fc8 100644
--- a/crates/googletest/src/matchers/empty_matcher.rs
+++ b/crates/googletest/src/matchers/empty_matcher.rs
@@ -14,15 +14,15 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches an empty container.
///
-/// `T` can be any container such that `&T` implements `IntoIterator`. For
-/// instance, `T` can be a common container like `Vec` and
-/// [`HashSet`][std::collections::HashSet].
+/// `T` can be any container that implements `IntoIterator`. For instance, `T`
+/// can be the reference of a common container like `&Vec` and
+/// [`&HashSet`][std::collections::HashSet].
///
/// ```
/// # use googletest::prelude::*;
@@ -32,42 +32,24 @@
/// verify_that!(value, empty())?;
/// let value: HashSet<i32> = HashSet::new();
/// verify_that!(value, empty())?;
-/// # Ok(())
-/// # }
-/// # should_pass().unwrap();
-/// ```
-///
-/// One can also check whether a slice is empty by dereferencing it:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # use std::collections::HashSet;
-/// # fn should_pass() -> Result<()> {
/// let value: &[u32] = &[];
-/// verify_that!(*value, empty())?;
+/// verify_that!(value, empty())?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
-
-pub fn empty<T: Debug + ?Sized>() -> impl Matcher<ActualT = T>
-where
- for<'a> &'a T: IntoIterator,
-{
- EmptyMatcher { phantom: Default::default() }
+pub fn empty() -> EmptyMatcher {
+ EmptyMatcher
}
-struct EmptyMatcher<T: ?Sized> {
- phantom: PhantomData<T>,
-}
+#[derive(MatcherBase)]
+pub struct EmptyMatcher;
-impl<T: Debug + ?Sized> Matcher for EmptyMatcher<T>
+impl<T: Debug + Copy> Matcher<T> for EmptyMatcher
where
- for<'a> &'a T: IntoIterator,
+ T: IntoIterator,
{
- type ActualT = T;
-
- fn matches(&self, actual: &T) -> MatcherResult {
+ fn matches(&self, actual: T) -> MatcherResult {
actual.into_iter().next().is_none().into()
}
@@ -78,7 +60,6 @@
#[cfg(test)]
mod tests {
- use super::empty;
use crate::prelude::*;
use std::collections::HashSet;
@@ -97,7 +78,7 @@
#[test]
fn empty_matcher_matches_empty_slice() -> Result<()> {
let value: &[i32] = &[];
- verify_that!(*value, empty())
+ verify_that!(value, empty())
}
#[test]
diff --git a/crates/googletest/src/matchers/eq_deref_of_matcher.rs b/crates/googletest/src/matchers/eq_deref_of_matcher.rs
deleted file mode 100644
index 320aafb..0000000
--- a/crates/googletest/src/matchers/eq_deref_of_matcher.rs
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2023 Google LLC
-//
-// 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.
-
-use crate::{
- description::Description,
- matcher::{Matcher, MatcherResult},
- matcher_support::{edit_distance, summarize_diff::create_diff},
-};
-use std::{fmt::Debug, marker::PhantomData, ops::Deref};
-
-/// Matches a value equal (in the sense of `==`) to the dereferenced value of
-/// `expected`.
-///
-/// This is similar to [`eq`][crate::matchers::eq] but takes a reference or
-/// smart pointer to the expected value rather than consuming it. This is useful
-/// when:
-///
-/// * one has only a reference to the expected value, and
-/// * the expected value cannot or should not be copied or cloned to create an
-/// owned value from it.
-///
-/// ```
-/// # use googletest::{matchers::eq_deref_of, verify_that};
-/// #[derive(Debug, PartialEq)]
-/// struct NonCloneableStruct(i32);
-/// let expected = NonCloneableStruct(123);
-/// verify_that!(NonCloneableStruct(123), eq_deref_of(&expected))
-/// # .unwrap()
-/// ```
-///
-/// **Note**: while one can use `eq_deref_of` with the configuration methods of
-/// [`StrMatcherConfigurator`][crate::matchers::str_matcher::StrMatcherConfigurator]
-/// to configure string equality, it is not possible to do so when the input is
-/// a smart pointer to a string.
-///
-/// ```compile_fail
-/// # use googletest::{matchers::{eq_deref_of, str_matcher::StrMatcherConfigurator}, verify_that};
-/// verify_that!("A string", eq_deref_of(Box::new("A STRING")).ignoring_ascii_case()) // Does not compile
-/// # .unwrap()
-/// ```
-///
-/// Otherwise, this has the same behaviour as [`eq`][crate::matchers::eq].
-pub fn eq_deref_of<ActualT: ?Sized, ExpectedRefT>(
- expected: ExpectedRefT,
-) -> EqDerefOfMatcher<ActualT, ExpectedRefT> {
- EqDerefOfMatcher { expected, phantom: Default::default() }
-}
-
-/// A matcher which matches a value equal to the derefenced value of `expected`.
-///
-/// See [`eq_deref_of`].
-pub struct EqDerefOfMatcher<ActualT: ?Sized, ExpectedRefT> {
- pub(crate) expected: ExpectedRefT,
- phantom: PhantomData<ActualT>,
-}
-
-impl<ActualT, ExpectedRefT, ExpectedT> Matcher for EqDerefOfMatcher<ActualT, ExpectedRefT>
-where
- ActualT: Debug + ?Sized,
- ExpectedRefT: Deref<Target = ExpectedT> + Debug,
- ExpectedT: PartialEq<ActualT> + Debug,
-{
- type ActualT = ActualT;
-
- fn matches(&self, actual: &ActualT) -> MatcherResult {
- (self.expected.deref() == actual).into()
- }
-
- fn describe(&self, matcher_result: MatcherResult) -> Description {
- match matcher_result {
- MatcherResult::Match => format!("is equal to {:?}", self.expected).into(),
- MatcherResult::NoMatch => format!("isn't equal to {:?}", self.expected).into(),
- }
- }
-
- fn explain_match(&self, actual: &ActualT) -> Description {
- format!(
- "which {}{}",
- &self.describe(self.matches(actual)),
- create_diff(
- &format!("{:#?}", actual),
- &format!("{:#?}", self.expected.deref()),
- edit_distance::Mode::Exact,
- )
- )
- .into()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::eq_deref_of;
- use crate::prelude::*;
- use indoc::indoc;
-
- #[derive(Debug, PartialEq)]
- struct NonCloneNonCopyStruct(i32);
-
- #[test]
- fn matches_value_with_ref_to_equal_value() -> Result<()> {
- verify_that!(NonCloneNonCopyStruct(123), eq_deref_of(&NonCloneNonCopyStruct(123)))
- }
-
- #[test]
- fn matches_value_with_box_of_equal_value() -> Result<()> {
- verify_that!(NonCloneNonCopyStruct(123), eq_deref_of(Box::new(NonCloneNonCopyStruct(123))))
- }
-
- #[test]
- fn does_not_match_value_with_non_equal_value() -> Result<()> {
- verify_that!(NonCloneNonCopyStruct(123), not(eq_deref_of(&NonCloneNonCopyStruct(234))))
- }
-
- #[test]
- fn shows_structured_diff() -> Result<()> {
- #[derive(Debug, PartialEq)]
- struct Strukt {
- int: i32,
- string: String,
- }
-
- let result = verify_that!(
- Strukt { int: 123, string: "something".into() },
- eq_deref_of(Box::new(Strukt { int: 321, string: "someone".into() }))
- );
- verify_that!(
- result,
- err(displays_as(contains_substring(indoc! {
- "
- Actual: Strukt { int: 123, string: \"something\" },
- which isn't equal to Strukt { int: 321, string: \"someone\" }
- Difference(-actual / +expected):
- Strukt {
- - int: 123,
- + int: 321,
- - string: \"something\",
- + string: \"someone\",
- }
- "})))
- )
- }
-}
diff --git a/crates/googletest/src/matchers/eq_matcher.rs b/crates/googletest/src/matchers/eq_matcher.rs
index 985307f..c39b79b 100644
--- a/crates/googletest/src/matchers/eq_matcher.rs
+++ b/crates/googletest/src/matchers/eq_matcher.rs
@@ -13,11 +13,11 @@
// limitations under the License.
use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use crate::matcher_support::edit_distance;
use crate::matcher_support::summarize_diff::create_diff;
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches a value equal (in the sense of `==`) to `expected`.
///
@@ -71,23 +71,21 @@
/// options on how equality is checked through the
/// [`StrMatcherConfigurator`][crate::matchers::str_matcher::StrMatcherConfigurator]
/// extension trait, which is implemented for this matcher.
-pub fn eq<A: ?Sized, T>(expected: T) -> EqMatcher<A, T> {
- EqMatcher { expected, phantom: Default::default() }
+pub fn eq<T>(expected: T) -> EqMatcher<T> {
+ EqMatcher { expected }
}
/// A matcher which matches a value equal to `expected`.
///
/// See [`eq`].
-pub struct EqMatcher<A: ?Sized, T> {
+#[derive(MatcherBase)]
+pub struct EqMatcher<T> {
pub(crate) expected: T,
- phantom: PhantomData<A>,
}
-impl<T: Debug, A: Debug + ?Sized + PartialEq<T>> Matcher for EqMatcher<A, T> {
- type ActualT = A;
-
- fn matches(&self, actual: &A) -> MatcherResult {
- (*actual == self.expected).into()
+impl<T: Debug, A: Debug + Copy + PartialEq<T>> Matcher<A> for EqMatcher<T> {
+ fn matches(&self, actual: A) -> MatcherResult {
+ (actual == self.expected).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -97,10 +95,10 @@
}
}
- fn explain_match(&self, actual: &A) -> Description {
+ fn explain_match(&self, actual: A) -> Description {
let expected_debug = format!("{:#?}", self.expected);
let actual_debug = format!("{:#?}", actual);
- let description = self.describe(self.matches(actual));
+ let description = Matcher::<A>::describe(self, self.matches(actual));
let diff = if is_multiline_string_debug(&actual_debug)
&& is_multiline_string_debug(&expected_debug)
@@ -118,7 +116,11 @@
create_diff(&actual_debug, &expected_debug, edit_distance::Mode::Exact)
};
- format!("which {description}{diff}").into()
+ if diff.is_empty() {
+ format!("which {description}").into()
+ } else {
+ format!("which {description}\n\n{diff}").into()
+ }
}
}
@@ -135,7 +137,6 @@
#[cfg(test)]
mod tests {
- use super::eq;
use crate::prelude::*;
use indoc::indoc;
@@ -171,7 +172,7 @@
let result = verify_that!(
Strukt { int: 123, string: "something".into() },
- eq(Strukt { int: 321, string: "someone".into() })
+ eq(&Strukt { int: 321, string: "someone".into() })
);
verify_that!(
result,
@@ -179,6 +180,7 @@
"
Actual: Strukt { int: 123, string: \"something\" },
which isn't equal to Strukt { int: 321, string: \"someone\" }
+
Difference(-actual / +expected):
Strukt {
- int: 123,
@@ -192,7 +194,7 @@
#[test]
fn eq_vec_debug_diff() -> Result<()> {
- let result = verify_that!(vec![1, 2, 3], eq(vec![1, 3, 4]));
+ let result = verify_that!(vec![1, 2, 3], eq(&vec![1, 3, 4]));
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
@@ -201,6 +203,7 @@
Expected: is equal to [1, 3, 4]
Actual: [1, 2, 3],
which isn't equal to [1, 3, 4]
+
Difference(-actual / +expected):
[
1,
@@ -214,7 +217,7 @@
#[test]
fn eq_vec_debug_diff_length_mismatch() -> Result<()> {
- let result = verify_that!(vec![1, 2, 3, 4, 5], eq(vec![1, 3, 5]));
+ let result = verify_that!(vec![1, 2, 3, 4, 5], eq(&vec![1, 3, 5]));
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
@@ -223,6 +226,7 @@
Expected: is equal to [1, 3, 5]
Actual: [1, 2, 3, 4, 5],
which isn't equal to [1, 3, 5]
+
Difference(-actual / +expected):
[
1,
@@ -237,13 +241,14 @@
#[test]
fn eq_debug_diff_common_lines_omitted() -> Result<()> {
- let result = verify_that!((1..50).collect::<Vec<_>>(), eq((3..52).collect::<Vec<_>>()));
+ let result = verify_that!((1..50).collect::<Vec<_>>(), eq(&(3..52).collect::<Vec<_>>()));
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
"
],
which isn't equal to [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
+
Difference(-actual / +expected):
[
- 1,
@@ -261,13 +266,14 @@
#[test]
fn eq_debug_diff_5_common_lines_not_omitted() -> Result<()> {
- let result = verify_that!((1..8).collect::<Vec<_>>(), eq((3..10).collect::<Vec<_>>()));
+ let result = verify_that!((1..8).collect::<Vec<_>>(), eq(&(3..10).collect::<Vec<_>>()));
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
"
Actual: [1, 2, 3, 4, 5, 6, 7],
which isn't equal to [3, 4, 5, 6, 7, 8, 9]
+
Difference(-actual / +expected):
[
- 1,
@@ -285,13 +291,14 @@
#[test]
fn eq_debug_diff_start_common_lines_omitted() -> Result<()> {
- let result = verify_that!((1..50).collect::<Vec<_>>(), eq((1..52).collect::<Vec<_>>()));
+ let result = verify_that!((1..50).collect::<Vec<_>>(), eq(&(1..52).collect::<Vec<_>>()));
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
"
],
which isn't equal to [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
+
Difference(-actual / +expected):
[
1,
@@ -306,13 +313,14 @@
#[test]
fn eq_debug_diff_end_common_lines_omitted() -> Result<()> {
- let result = verify_that!((1..52).collect::<Vec<_>>(), eq((3..52).collect::<Vec<_>>()));
+ let result = verify_that!((1..52).collect::<Vec<_>>(), eq(&(3..52).collect::<Vec<_>>()));
verify_that!(
result,
err(displays_as(contains_substring(indoc! {
"
],
which isn't equal to [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
+
Difference(-actual / +expected):
[
- 1,
diff --git a/crates/googletest/src/matchers/err_matcher.rs b/crates/googletest/src/matchers/err_matcher.rs
index 3b10de4..aa88045 100644
--- a/crates/googletest/src/matchers/err_matcher.rs
+++ b/crates/googletest/src/matchers/err_matcher.rs
@@ -14,9 +14,9 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches a `Result` containing `Err` with a value matched by `inner`.
///
@@ -38,28 +38,53 @@
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// ```
-pub fn err<T: Debug, E: Debug>(
- inner: impl Matcher<ActualT = E>,
-) -> impl Matcher<ActualT = std::result::Result<T, E>> {
- ErrMatcher::<T, E, _> { inner, phantom_t: Default::default(), phantom_e: Default::default() }
+pub fn err<Inner>(inner: Inner) -> ErrMatcher<Inner> {
+ ErrMatcher { inner }
}
-struct ErrMatcher<T, E, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct ErrMatcher<InnerMatcherT> {
inner: InnerMatcherT,
- phantom_t: PhantomData<T>,
- phantom_e: PhantomData<E>,
}
-impl<T: Debug, E: Debug, InnerMatcherT: Matcher<ActualT = E>> Matcher
- for ErrMatcher<T, E, InnerMatcherT>
+impl<T: Debug + Copy, E: Debug + Copy, InnerMatcherT: Matcher<E>> Matcher<std::result::Result<T, E>>
+ for ErrMatcher<InnerMatcherT>
{
- type ActualT = std::result::Result<T, E>;
+ fn matches(&self, actual: std::result::Result<T, E>) -> MatcherResult {
+ actual.err().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
+ }
- fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+ fn explain_match(&self, actual: std::result::Result<T, E>) -> Description {
+ match actual {
+ Err(e) => {
+ Description::new().text("which is an error").nested(self.inner.explain_match(e))
+ }
+ Ok(_) => "which is a success".into(),
+ }
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ match matcher_result {
+ MatcherResult::Match => {
+ format!("is an error which {}", self.inner.describe(MatcherResult::Match)).into()
+ }
+ MatcherResult::NoMatch => format!(
+ "is a success or is an error containing a value which {}",
+ self.inner.describe(MatcherResult::NoMatch)
+ )
+ .into(),
+ }
+ }
+}
+
+impl<'a, T: Debug, E: Debug, InnerMatcherT: Matcher<&'a E>> Matcher<&'a std::result::Result<T, E>>
+ for ErrMatcher<InnerMatcherT>
+{
+ fn matches(&self, actual: &'a std::result::Result<T, E>) -> MatcherResult {
actual.as_ref().err().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
}
- fn explain_match(&self, actual: &Self::ActualT) -> Description {
+ fn explain_match(&self, actual: &'a std::result::Result<T, E>) -> Description {
match actual {
Err(e) => {
Description::new().text("which is an error").nested(self.inner.explain_match(e))
@@ -84,8 +109,7 @@
#[cfg(test)]
mod tests {
- use super::err;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
@@ -94,7 +118,7 @@
let matcher = err(eq(1));
let value: std::result::Result<i32, i32> = Err(1);
- let result = matcher.matches(&value);
+ let result = matcher.matches(value);
verify_that!(result, eq(MatcherResult::Match))
}
@@ -104,7 +128,7 @@
let matcher = err(eq(1));
let value: std::result::Result<i32, i32> = Err(0);
- let result = matcher.matches(&value);
+ let result = matcher.matches(value);
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -114,12 +138,30 @@
let matcher = err(eq(1));
let value: std::result::Result<i32, i32> = Ok(1);
- let result = matcher.matches(&value);
+ let result = matcher.matches(value);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
+ fn err_matches_result_with_err_value_by_ref() -> Result<()> {
+ let value: std::result::Result<String, String> = Err("123".to_string());
+ verify_that!(value, err(eq("123")))
+ }
+
+ #[test]
+ fn err_does_not_match_result_with_wrong_err_value_by_ref() -> Result<()> {
+ let value: std::result::Result<String, String> = Err("321".to_string());
+ verify_that!(value, not(err(eq("123"))))
+ }
+
+ #[test]
+ fn err_does_not_match_result_with_ok_by_ref() -> Result<()> {
+ let value: std::result::Result<String, String> = Ok("123".to_string());
+ verify_that!(value, not(err(eq("123"))))
+ }
+
+ #[test]
fn err_full_error_message() -> Result<()> {
let result = verify_that!(Err::<i32, i32>(1), err(eq(2)));
@@ -138,15 +180,102 @@
}
#[test]
- fn err_describe_matches() -> Result<()> {
- let matcher = super::ErrMatcher::<i32, i32, _> {
- inner: eq(1),
- phantom_t: Default::default(),
- phantom_e: Default::default(),
- };
+ fn err_describe_match() -> Result<()> {
+ let matcher = err(eq(1));
verify_that!(
- matcher.describe(MatcherResult::Match),
+ Matcher::<std::result::Result<i32, i32>>::describe(&matcher, MatcherResult::Match),
displays_as(eq("is an error which is equal to 1"))
)
}
+
+ #[test]
+ fn err_describe_no_match() -> Result<()> {
+ let matcher = err(eq(1));
+ verify_that!(
+ Matcher::<std::result::Result<i32, i32>>::describe(&matcher, MatcherResult::NoMatch),
+ displays_as(eq(
+ "is a success or is an error containing a value which isn't equal to 1"
+ ))
+ )
+ }
+
+ #[test]
+ fn err_describe_match_by_ref() -> Result<()> {
+ let matcher = err(eq("123"));
+ verify_that!(
+ Matcher::<&std::result::Result<String, String>>::describe(
+ &matcher,
+ MatcherResult::Match
+ ),
+ displays_as(eq("is an error which is equal to \"123\""))
+ )
+ }
+
+ #[test]
+ fn err_describe_no_match_by_ref() -> Result<()> {
+ let matcher = err(eq("123"));
+ verify_that!(
+ Matcher::<&std::result::Result<String, String>>::describe(
+ &matcher,
+ MatcherResult::NoMatch
+ ),
+ displays_as(eq(
+ "is a success or is an error containing a value which isn't equal to \"123\""
+ ))
+ )
+ }
+
+ #[test]
+ fn err_explain_match_success() -> Result<()> {
+ let actual: std::result::Result<i32, i32> = Err(1);
+ let matcher = err(eq(1));
+ verify_that!(
+ matcher.explain_match(actual),
+ displays_as(eq("which is an error\n which is equal to 1"))
+ )
+ }
+
+ #[test]
+ fn err_explain_match_fail() -> Result<()> {
+ let actual: std::result::Result<i32, i32> = Err(2);
+ let matcher = err(eq(1));
+ verify_that!(
+ matcher.explain_match(actual),
+ displays_as(eq("which is an error\n which isn't equal to 1"))
+ )
+ }
+
+ #[test]
+ fn err_explain_match_ok() -> Result<()> {
+ let actual: std::result::Result<i32, i32> = Ok(1);
+ let matcher = err(eq(1));
+ verify_that!(matcher.explain_match(actual), displays_as(eq("which is a success")))
+ }
+
+ #[test]
+ fn err_explain_match_success_by_ref() -> Result<()> {
+ let actual: std::result::Result<String, String> = Err("123".to_string());
+ let matcher = err(eq("123"));
+ verify_that!(
+ matcher.explain_match(&actual),
+ displays_as(eq("which is an error\n which is equal to \"123\""))
+ )
+ }
+
+ #[test]
+ fn err_explain_match_fail_by_ref() -> Result<()> {
+ let actual: std::result::Result<String, String> = Err("321".to_string());
+ let matcher = err(eq("123"));
+ verify_that!(
+ matcher.explain_match(&actual),
+ displays_as(eq("which is an error\n which isn't equal to \"123\""))
+ )
+ }
+
+ #[test]
+ fn err_explain_match_ok_by_ref() -> Result<()> {
+ let actual: std::result::Result<String, String> = Ok("123".to_string());
+ let matcher = err(eq("123"));
+ verify_that!(matcher.explain_match(&actual), displays_as(eq("which is a success")))
+ }
}
diff --git a/crates/googletest/src/matchers/field_matcher.rs b/crates/googletest/src/matchers/field_matcher.rs
index fc37ce6..58a55c6 100644
--- a/crates/googletest/src/matchers/field_matcher.rs
+++ b/crates/googletest/src/matchers/field_matcher.rs
@@ -33,7 +33,7 @@
/// int: i32
/// }
/// # fn should_pass() -> Result<()> {
-/// verify_that!(IntField{int: 32}, field!(IntField.int, eq(32)))?;
+/// verify_that!(IntField{int: 32}, field!(&IntField.int, eq(32)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
@@ -46,7 +46,7 @@
/// #[derive(Debug)]
/// struct IntField(i32);
/// # fn should_pass() -> Result<()> {
-/// verify_that!(IntField(32), field!(IntField.0, eq(32)))?;
+/// verify_that!(IntField(32), field!(&IntField.0, eq(32)))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
@@ -63,11 +63,11 @@
/// B,
/// }
/// # fn should_pass() -> Result<()> {
-/// verify_that!(MyEnum::A(32), field!(MyEnum::A.0, eq(32)))?; // Passes
+/// verify_that!(MyEnum::A(32), field!(&MyEnum::A.0, eq(32)))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
-/// verify_that!(MyEnum::B, field!(MyEnum::A.0, eq(32)))?; // Fails: wrong enum variant
+/// verify_that!(MyEnum::B, field!(&MyEnum::A.0, eq(32)))?; // Fails: wrong enum variant
/// # Ok(())
/// # }
/// # should_pass().unwrap();
@@ -83,7 +83,22 @@
/// pub struct AStruct(pub i32);
/// }
/// # fn should_pass() -> Result<()> {
-/// verify_that!(a_module::AStruct(32), field!(a_module::AStruct.0, eq(32)))?;
+/// verify_that!(a_module::AStruct(32), field!(&a_module::AStruct.0, eq(32)))?;
+/// # Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// If the inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// struct IntField {
+/// int: i32
+/// }
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(IntField{int: 32}, field!(&IntField.int, 32))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
@@ -105,6 +120,67 @@
/// # }
/// ```
///
+/// # Specification of the field pattern
+///
+/// The specification of the field follow the syntax: `(ref)? (&)?
+/// $TYPE.$FIELD`.
+/// The `&` allows to specify whether this matcher matches against an actual of
+/// type `$TYPE` (`$TYPE`` must implement `Copy`) or a `&$TYPE`.
+///
+/// For instance:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// pub struct AStruct{a_field: i32};
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct{a_field: 32}, field!(&AStruct.a_field, eq(32)))?;
+/// # Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug, Clone, Copy)]
+/// pub struct AStruct{a_field: i32};
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct{a_field: 32}, field!(AStruct.a_field, eq(32)))?;
+/// # Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// The `ref` allows to bind the field value by reference, which is required if
+/// the field type does not implement `Copy`.
+///
+/// For instance:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// pub struct AStruct{a_field: i32};
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct{a_field: 32}, field!(&AStruct.a_field, eq(32)))?;
+/// # Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// If `field!` is qualified by both `&` and `ref`, they can both be omitted.
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// pub struct AStruct{a_field: String};
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct{a_field: "32".into()}, field!(&AStruct.a_field, ref eq("32")))?;
+/// verify_that!(AStruct{a_field: "32".into()}, field!(AStruct.a_field, eq("32")))?;
+/// # Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
/// See also the macro [`property`][crate::matchers::property] for an analogous
/// mechanism to extract a datum by invoking a method.
#[macro_export]
@@ -115,15 +191,16 @@
// Internal-only macro created so that the macro definition does not appear in
// generated documentation.
+// We cannot use `path` or `ty` to capture the type as we are terminating the
+// type with a . (dot).
#[doc(hidden)]
#[macro_export]
macro_rules! field_internal {
- ($($t:ident)::+.$field:tt, $m:expr) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher;
- field_matcher(
- |o| {
+ (&$($t:ident)::+.$field:tt, ref $m:expr) => {{
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher(
+ |o: &_| {
match o {
- $($t)::* { $field: value, .. } => Some(value),
+ &$($t)::* {$field: ref value, .. } => Some(value),
// The pattern below is unreachable if the type is a struct (as opposed to an
// enum). Since the macro can't know which it is, we always include it and just
// tell the compiler not to complain.
@@ -132,7 +209,37 @@
}
},
&stringify!($field),
- $m)
+ $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
+ }};
+ (&$($t:ident)::+.$field:tt, $m:expr) => {{
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher(
+ |o: &&_| {
+ match o {
+ &$($t)::* {$field: value, .. } => Some(value),
+ // The pattern below is unreachable if the type is a struct (as opposed to an
+ // enum). Since the macro can't know which it is, we always include it and just
+ // tell the compiler not to complain.
+ #[allow(unreachable_patterns)]
+ _ => None,
+ }
+ },
+ &stringify!($field),
+ $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
+ }};
+ ($($t:ident)::+.$field:tt, $m:expr) => {{
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::field_matcher(
+ |o: &_| {
+ match o {
+ $($t)::* {$field: value, .. } => Some(value),
+ // The pattern below is unreachable if the type is a struct (as opposed to an
+ // enum). Since the macro can't know which it is, we always include it and just
+ // tell the compiler not to complain.
+ #[allow(unreachable_patterns)]
+ _ => None,
+ }
+ },
+ &stringify!($field),
+ $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
}};
}
@@ -143,7 +250,7 @@
pub mod internal {
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
@@ -152,26 +259,25 @@
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
- pub fn field_matcher<OuterT: Debug, InnerT: Debug, InnerMatcher: Matcher<ActualT = InnerT>>(
+ pub fn field_matcher<OuterT, InnerT, InnerMatcher>(
field_accessor: fn(&OuterT) -> Option<&InnerT>,
field_path: &'static str,
inner: InnerMatcher,
- ) -> impl Matcher<ActualT = OuterT> {
+ ) -> FieldMatcher<OuterT, InnerT, InnerMatcher> {
FieldMatcher { field_accessor, field_path, inner }
}
- struct FieldMatcher<OuterT, InnerT, InnerMatcher> {
+ #[derive(MatcherBase)]
+ pub struct FieldMatcher<OuterT, InnerT, InnerMatcher> {
field_accessor: fn(&OuterT) -> Option<&InnerT>,
field_path: &'static str,
inner: InnerMatcher,
}
- impl<OuterT: Debug, InnerT: Debug, InnerMatcher: Matcher<ActualT = InnerT>> Matcher
- for FieldMatcher<OuterT, InnerT, InnerMatcher>
+ impl<'a, OuterT: Debug + 'a, InnerT: Debug + 'a, InnerMatcher: Matcher<&'a InnerT>>
+ Matcher<&'a OuterT> for FieldMatcher<OuterT, InnerT, InnerMatcher>
{
- type ActualT = OuterT;
-
- fn matches(&self, actual: &OuterT) -> MatcherResult {
+ fn matches(&self, actual: &'a OuterT) -> MatcherResult {
if let Some(value) = (self.field_accessor)(actual) {
self.inner.matches(value)
} else {
@@ -179,7 +285,7 @@
}
}
- fn explain_match(&self, actual: &OuterT) -> Description {
+ fn explain_match(&self, actual: &'a OuterT) -> Description {
if let Some(actual) = (self.field_accessor)(actual) {
format!(
"which has field `{}`, {}",
@@ -204,4 +310,41 @@
.into()
}
}
+
+ impl<OuterT: Debug + Copy, InnerT: Debug + Copy, InnerMatcher: Matcher<InnerT>> Matcher<OuterT>
+ for FieldMatcher<OuterT, InnerT, InnerMatcher>
+ {
+ fn matches(&self, actual: OuterT) -> MatcherResult {
+ if let Some(value) = (self.field_accessor)(&actual) {
+ self.inner.matches(*value)
+ } else {
+ MatcherResult::NoMatch
+ }
+ }
+
+ fn explain_match(&self, actual: OuterT) -> Description {
+ if let Some(actual) = (self.field_accessor)(&actual) {
+ format!(
+ "which has field `{}`, {}",
+ self.field_path,
+ self.inner.explain_match(*actual)
+ )
+ .into()
+ } else {
+ let formatted_actual_value = format!("{actual:?}");
+ let without_fields = formatted_actual_value.split('(').next().unwrap_or("");
+ let without_fields = without_fields.split('{').next().unwrap_or("").trim_end();
+ format!("which has the wrong enum variant `{without_fields}`").into()
+ }
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ format!(
+ "has field `{}`, which {}",
+ self.field_path,
+ self.inner.describe(matcher_result)
+ )
+ .into()
+ }
+ }
}
diff --git a/crates/googletest/src/matchers/ge_matcher.rs b/crates/googletest/src/matchers/ge_matcher.rs
index cb2a91f..4919f2c 100644
--- a/crates/googletest/src/matchers/ge_matcher.rs
+++ b/crates/googletest/src/matchers/ge_matcher.rs
@@ -14,9 +14,9 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches a value greater than or equal to (in the sense of `>=`) `expected`.
///
@@ -74,24 +74,20 @@
///
/// You can find the standard library `PartialOrd` implementation in
/// <https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#implementors>
-pub fn ge<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug>(
- expected: ExpectedT,
-) -> impl Matcher<ActualT = ActualT> {
- GeMatcher::<ActualT, _> { expected, phantom: Default::default() }
+pub fn ge<ExpectedT>(expected: ExpectedT) -> GeMatcher<ExpectedT> {
+ GeMatcher { expected }
}
-struct GeMatcher<ActualT, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct GeMatcher<ExpectedT> {
expected: ExpectedT,
- phantom: PhantomData<ActualT>,
}
-impl<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug> Matcher
- for GeMatcher<ActualT, ExpectedT>
+impl<ActualT: Debug + PartialOrd<ExpectedT> + Copy, ExpectedT: Debug> Matcher<ActualT>
+ for GeMatcher<ExpectedT>
{
- type ActualT = ActualT;
-
- fn matches(&self, actual: &ActualT) -> MatcherResult {
- (*actual >= self.expected).into()
+ fn matches(&self, actual: ActualT) -> MatcherResult {
+ (actual >= self.expected).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -106,8 +102,7 @@
#[cfg(test)]
mod tests {
- use super::ge;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
use std::ffi::OsString;
@@ -122,7 +117,7 @@
#[test]
fn ge_does_not_match_smaller_i32() -> Result<()> {
let matcher = ge(10);
- let result = matcher.matches(&9);
+ let result = matcher.matches(9);
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -134,7 +129,7 @@
#[test]
fn ge_does_not_match_lesser_str() -> Result<()> {
let matcher = ge("z");
- let result = matcher.matches(&"a");
+ let result = matcher.matches("a");
verify_that!(result, eq(MatcherResult::NoMatch))
}
diff --git a/crates/googletest/src/matchers/gt_matcher.rs b/crates/googletest/src/matchers/gt_matcher.rs
index b52c6e3..123201f 100644
--- a/crates/googletest/src/matchers/gt_matcher.rs
+++ b/crates/googletest/src/matchers/gt_matcher.rs
@@ -14,9 +14,9 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches a value greater (in the sense of `>`) than `expected`.
///
@@ -74,24 +74,20 @@
///
/// You can find the standard library `PartialOrd` implementation in
/// <https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#implementors>
-pub fn gt<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug>(
- expected: ExpectedT,
-) -> impl Matcher<ActualT = ActualT> {
- GtMatcher::<ActualT, _> { expected, phantom: Default::default() }
+pub fn gt<ExpectedT: Debug>(expected: ExpectedT) -> GtMatcher<ExpectedT> {
+ GtMatcher { expected }
}
-struct GtMatcher<ActualT, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct GtMatcher<ExpectedT> {
expected: ExpectedT,
- phantom: PhantomData<ActualT>,
}
-impl<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug> Matcher
- for GtMatcher<ActualT, ExpectedT>
+impl<ActualT: Debug + PartialOrd<ExpectedT> + Copy, ExpectedT: Debug> Matcher<ActualT>
+ for GtMatcher<ExpectedT>
{
- type ActualT = ActualT;
-
- fn matches(&self, actual: &ActualT) -> MatcherResult {
- (*actual > self.expected).into()
+ fn matches(&self, actual: ActualT) -> MatcherResult {
+ (actual > self.expected).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -106,8 +102,7 @@
#[cfg(test)]
mod tests {
- use super::gt;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
use std::ffi::OsString;
@@ -122,14 +117,14 @@
#[test]
fn gt_does_not_match_equal_i32() -> Result<()> {
let matcher = gt(10);
- let result = matcher.matches(&10);
+ let result = matcher.matches(10);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn gt_does_not_match_lower_i32() -> Result<()> {
let matcher = gt(-50);
- let result = matcher.matches(&-51);
+ let result = matcher.matches(-51);
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -141,7 +136,7 @@
#[test]
fn gt_does_not_match_lesser_str() -> Result<()> {
let matcher = gt("B");
- let result = matcher.matches(&"A");
+ let result = matcher.matches("A");
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -163,7 +158,7 @@
#[test]
fn gt_mismatch_combined_with_each() -> Result<()> {
- let result = verify_that!(vec![19, 23, 11], each(gt(15)));
+ let result = verify_that!(vec![19, 23, 11], each(gt(&15)));
verify_that!(
result,
@@ -181,7 +176,7 @@
#[test]
fn gt_describe_matches() -> Result<()> {
verify_that!(
- gt::<i32, i32>(232).describe(MatcherResult::Match),
+ Matcher::<i32>::describe(>(232), MatcherResult::Match),
displays_as(eq("is greater than 232"))
)
}
@@ -189,7 +184,7 @@
#[test]
fn gt_describe_does_not_match() -> Result<()> {
verify_that!(
- gt::<i32, i32>(232).describe(MatcherResult::NoMatch),
+ Matcher::<i32>::describe(>(232), MatcherResult::NoMatch),
displays_as(eq("is less than or equal to 232"))
)
}
diff --git a/crates/googletest/src/matchers/has_entry_matcher.rs b/crates/googletest/src/matchers/has_entry_matcher.rs
index 7f9d2ad..b5cce71 100644
--- a/crates/googletest/src/matchers/has_entry_matcher.rs
+++ b/crates/googletest/src/matchers/has_entry_matcher.rs
@@ -13,31 +13,30 @@
// limitations under the License.
use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
-use std::marker::PhantomData;
-/// Matches a HashMap containing the given `key` whose value is matched by the
-/// matcher `inner`.
+/// Matches a `&HashMap` containing the given `key` whose value is matched by
+/// the matcher `inner`.
///
/// ```
/// # use googletest::prelude::*;
/// # use std::collections::HashMap;
/// # fn should_pass() -> Result<()> {
/// let value = HashMap::from([(0, 1), (1, -1)]);
-/// verify_that!(value, has_entry(0, eq(1)))?; // Passes
+/// verify_that!(value, has_entry(0, eq(&1)))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
/// # let value = HashMap::from([(0, 1), (1, -1)]);
-/// verify_that!(value, has_entry(1, gt(0)))?; // Fails: value not matched
+/// verify_that!(value, has_entry(1, gt(&0)))?; // Fails: value not matched
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
/// # let value = HashMap::from([(0, 1), (1, -1)]);
-/// verify_that!(value, has_entry(2, eq(0)))?; // Fails: key not present
+/// verify_that!(value, has_entry(2, eq(&0)))?; // Fails: key not present
/// # Ok(())
/// # }
/// # should_pass().unwrap();
@@ -45,42 +44,33 @@
/// # should_fail_2().unwrap_err();
/// ```
///
-/// Note: One could obtain the same effect by collecting entries into a `Vec`
-/// and using `contains`:
+/// Note: One could obtain the same effect by using `contains` and a
+/// `Matcher<(&Key, &Value)>`:
///
/// ```
/// # use googletest::prelude::*;
/// # use std::collections::HashMap;
/// # fn should_pass() -> Result<()> {
/// let value = HashMap::from([(0, 1), (1, -1)]);
-/// verify_that!(value.into_iter().collect::<Vec<_>>(), contains(eq((0, 1))))?;
+/// verify_that!(value, contains(eq((&0, &1))))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
-///
-/// However, `has_entry` will offer somewhat better diagnostic messages in the
-/// case of assertion failure. And it avoid the extra allocation hidden in the
-/// code above.
-pub fn has_entry<KeyT: Debug + Eq + Hash, ValueT: Debug, MatcherT: Matcher<ActualT = ValueT>>(
- key: KeyT,
- inner: MatcherT,
-) -> impl Matcher<ActualT = HashMap<KeyT, ValueT>> {
- HasEntryMatcher { key, inner, phantom: Default::default() }
+pub fn has_entry<KeyT, MatcherT>(key: KeyT, inner: MatcherT) -> HasEntryMatcher<KeyT, MatcherT> {
+ HasEntryMatcher { key, inner }
}
-struct HasEntryMatcher<KeyT, ValueT, MatcherT> {
+#[derive(MatcherBase)]
+pub struct HasEntryMatcher<KeyT, MatcherT> {
key: KeyT,
inner: MatcherT,
- phantom: PhantomData<ValueT>,
}
-impl<KeyT: Debug + Eq + Hash, ValueT: Debug, MatcherT: Matcher<ActualT = ValueT>> Matcher
- for HasEntryMatcher<KeyT, ValueT, MatcherT>
+impl<'a, KeyT: Debug + Eq + Hash, ValueT: Debug, MatcherT: Matcher<&'a ValueT>>
+ Matcher<&'a HashMap<KeyT, ValueT>> for HasEntryMatcher<KeyT, MatcherT>
{
- type ActualT = HashMap<KeyT, ValueT>;
-
- fn matches(&self, actual: &HashMap<KeyT, ValueT>) -> MatcherResult {
+ fn matches(&self, actual: &'a HashMap<KeyT, ValueT>) -> MatcherResult {
if let Some(value) = actual.get(&self.key) {
self.inner.matches(value)
} else {
@@ -88,7 +78,7 @@
}
}
- fn explain_match(&self, actual: &HashMap<KeyT, ValueT>) -> Description {
+ fn explain_match(&self, actual: &'a HashMap<KeyT, ValueT>) -> Description {
if let Some(value) = actual.get(&self.key) {
format!(
"which contains key {:?}, but is mapped to value {:#?}, {}",
@@ -123,7 +113,6 @@
#[cfg(test)]
mod tests {
- use super::has_entry;
use crate::prelude::*;
use indoc::indoc;
use std::collections::HashMap;
@@ -131,30 +120,30 @@
#[test]
fn has_entry_does_not_match_empty_hash_map() -> Result<()> {
let value: HashMap<i32, i32> = HashMap::new();
- verify_that!(value, not(has_entry(0, eq(0))))
+ verify_that!(&value, not(has_entry(0, eq(&0))))
}
#[test]
fn has_entry_matches_hash_map_with_value() -> Result<()> {
let value: HashMap<i32, i32> = HashMap::from([(0, 0)]);
- verify_that!(value, has_entry(0, eq(0)))
+ verify_that!(&value, has_entry(0, eq(&0)))
}
#[test]
fn has_entry_does_not_match_hash_map_with_wrong_value() -> Result<()> {
let value: HashMap<i32, i32> = HashMap::from([(0, 1)]);
- verify_that!(value, not(has_entry(0, eq(0))))
+ verify_that!(&value, not(has_entry(0, eq(&0))))
}
#[test]
fn has_entry_does_not_match_hash_map_with_wrong_key() -> Result<()> {
let value: HashMap<i32, i32> = HashMap::from([(1, 0)]);
- verify_that!(value, not(has_entry(0, eq(0))))
+ verify_that!(&value, not(has_entry(0, eq(&0))))
}
#[test]
fn has_entry_shows_correct_message_when_key_is_not_present() -> Result<()> {
- let result = verify_that!(HashMap::from([(0, 0)]), has_entry(1, eq(0)));
+ let result = verify_that!(HashMap::from([(0, 0)]), has_entry(1, eq(&0)));
verify_that!(
result,
@@ -171,7 +160,7 @@
#[test]
fn has_entry_shows_correct_message_when_key_has_non_matching_value() -> Result<()> {
- let result = verify_that!(HashMap::from([(0, 0)]), has_entry(0, eq(1)));
+ let result = verify_that!(HashMap::from([(0, 0)]), has_entry(0, eq(&1)));
verify_that!(
result,
diff --git a/crates/googletest/src/matchers/is_encoded_string_matcher.rs b/crates/googletest/src/matchers/is_encoded_string_matcher.rs
index d2fb259..098658b 100644
--- a/crates/googletest/src/matchers/is_encoded_string_matcher.rs
+++ b/crates/googletest/src/matchers/is_encoded_string_matcher.rs
@@ -14,9 +14,9 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches a byte sequence which is a UTF-8 encoded string matched by `inner`.
///
@@ -48,30 +48,26 @@
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// ```
-pub fn is_utf8_string<'a, ActualT: AsRef<[u8]> + Debug + 'a, InnerMatcherT>(
- inner: InnerMatcherT,
-) -> impl Matcher<ActualT = ActualT>
+pub fn is_utf8_string<InnerMatcherT>(inner: InnerMatcherT) -> IsEncodedStringMatcher<InnerMatcherT>
where
- InnerMatcherT: Matcher<ActualT = String>,
+ InnerMatcherT: for<'a> Matcher<&'a str>,
{
- IsEncodedStringMatcher { inner, phantom: Default::default() }
+ IsEncodedStringMatcher { inner }
}
-struct IsEncodedStringMatcher<ActualT, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct IsEncodedStringMatcher<InnerMatcherT> {
inner: InnerMatcherT,
- phantom: PhantomData<ActualT>,
}
-impl<'a, ActualT: AsRef<[u8]> + Debug + 'a, InnerMatcherT> Matcher
- for IsEncodedStringMatcher<ActualT, InnerMatcherT>
+impl<ActualT: AsRef<[u8]> + Debug + Copy, InnerMatcherT> Matcher<ActualT>
+ for IsEncodedStringMatcher<InnerMatcherT>
where
- InnerMatcherT: Matcher<ActualT = String>,
+ InnerMatcherT: for<'a> Matcher<&'a str>,
{
- type ActualT = ActualT;
-
- fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
- String::from_utf8(actual.as_ref().to_vec())
- .map(|s| self.inner.matches(&s))
+ fn matches(&self, actual: ActualT) -> MatcherResult {
+ std::str::from_utf8(actual.as_ref())
+ .map(|s| self.inner.matches(s))
.unwrap_or(MatcherResult::NoMatch)
}
@@ -90,10 +86,10 @@
}
}
- fn explain_match(&self, actual: &Self::ActualT) -> Description {
- match String::from_utf8(actual.as_ref().to_vec()) {
+ fn explain_match(&self, actual: ActualT) -> Description {
+ match std::str::from_utf8(actual.as_ref()) {
Ok(s) => {
- format!("which is a UTF-8 encoded string {}", self.inner.explain_match(&s)).into()
+ format!("which is a UTF-8 encoded string {}", self.inner.explain_match(s)).into()
}
Err(e) => format!("which is not a UTF-8 encoded string: {e}").into(),
}
@@ -132,27 +128,27 @@
#[test]
fn has_correct_description_in_matched_case() -> Result<()> {
- let matcher = is_utf8_string::<&[u8], _>(eq("A string"));
+ let matcher = is_utf8_string(eq("A string"));
verify_that!(
- matcher.describe(MatcherResult::Match),
+ Matcher::<&[u8]>::describe(&matcher, MatcherResult::Match),
displays_as(eq("is a UTF-8 encoded string which is equal to \"A string\""))
)
}
#[test]
fn has_correct_description_in_not_matched_case() -> Result<()> {
- let matcher = is_utf8_string::<&[u8], _>(eq("A string"));
+ let matcher = is_utf8_string(eq("A string"));
verify_that!(
- matcher.describe(MatcherResult::NoMatch),
+ Matcher::<&[u8]>::describe(&matcher, MatcherResult::NoMatch),
displays_as(eq("is not a UTF-8 encoded string which is equal to \"A string\""))
)
}
#[test]
fn has_correct_explanation_in_matched_case() -> Result<()> {
- let explanation = is_utf8_string(eq("A string")).explain_match(&"A string".as_bytes());
+ let explanation = is_utf8_string(eq("A string")).explain_match("A string".as_bytes());
verify_that!(
explanation,
@@ -162,15 +158,14 @@
#[test]
fn has_correct_explanation_when_byte_array_is_not_utf8_encoded() -> Result<()> {
- let explanation = is_utf8_string(eq("A string")).explain_match(&&[192, 128, 0, 64]);
+ let explanation = is_utf8_string(eq("A string")).explain_match([192, 128, 0, 64]);
verify_that!(explanation, displays_as(starts_with("which is not a UTF-8 encoded string: ")))
}
#[test]
fn has_correct_explanation_when_inner_matcher_does_not_match() -> Result<()> {
- let explanation =
- is_utf8_string(eq("A string")).explain_match(&"Another string".as_bytes());
+ let explanation = is_utf8_string(eq("A string")).explain_match("Another string".as_bytes());
verify_that!(
explanation,
diff --git a/crates/googletest/src/matchers/is_matcher.rs b/crates/googletest/src/matchers/is_matcher.rs
index 336ce53..3748858 100644
--- a/crates/googletest/src/matchers/is_matcher.rs
+++ b/crates/googletest/src/matchers/is_matcher.rs
@@ -16,34 +16,29 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches precisely values matched by `inner`.
///
/// The returned matcher produces a description prefixed by the string
/// `description`. This is useful in contexts where the test assertion failure
/// output must include the additional description.
-pub fn is<'a, ActualT: Debug + 'a, InnerMatcherT: Matcher<ActualT = ActualT> + 'a>(
- description: &'a str,
- inner: InnerMatcherT,
-) -> impl Matcher<ActualT = ActualT> + 'a {
- IsMatcher { description, inner, phantom: Default::default() }
+pub fn is<InnerMatcherT>(description: &str, inner: InnerMatcherT) -> IsMatcher<'_, InnerMatcherT> {
+ IsMatcher { description, inner }
}
-struct IsMatcher<'a, ActualT, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct IsMatcher<'a, InnerMatcherT> {
description: &'a str,
inner: InnerMatcherT,
- phantom: PhantomData<ActualT>,
}
-impl<'a, ActualT: Debug, InnerMatcherT: Matcher<ActualT = ActualT>> Matcher
- for IsMatcher<'a, ActualT, InnerMatcherT>
+impl<'a, ActualT: Debug + Copy, InnerMatcherT: Matcher<ActualT>> Matcher<ActualT>
+ for IsMatcher<'a, InnerMatcherT>
{
- type ActualT = ActualT;
-
- fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+ fn matches(&self, actual: ActualT) -> MatcherResult {
self.inner.matches(actual)
}
@@ -64,7 +59,7 @@
}
}
- fn explain_match(&self, actual: &Self::ActualT) -> Description {
+ fn explain_match(&self, actual: ActualT) -> Description {
self.inner.explain_match(actual)
}
}
diff --git a/crates/googletest/src/matchers/is_nan_matcher.rs b/crates/googletest/src/matchers/is_nan_matcher.rs
index 0af4338..f6fae89 100644
--- a/crates/googletest/src/matchers/is_nan_matcher.rs
+++ b/crates/googletest/src/matchers/is_nan_matcher.rs
@@ -14,22 +14,21 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
use num_traits::float::Float;
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches a floating point value which is NaN.
-pub fn is_nan<T: Float + Debug>() -> impl Matcher<ActualT = T> {
- IsNanMatcher::<T>(Default::default())
+pub fn is_nan() -> IsNanMatcher {
+ IsNanMatcher
}
-struct IsNanMatcher<T>(PhantomData<T>);
+#[derive(MatcherBase)]
+pub struct IsNanMatcher;
-impl<T: Float + Debug> Matcher for IsNanMatcher<T> {
- type ActualT = T;
-
- fn matches(&self, actual: &T) -> MatcherResult {
+impl<T: Float + Debug + Copy> Matcher<T> for IsNanMatcher {
+ fn matches(&self, actual: T) -> MatcherResult {
actual.is_nan().into()
}
@@ -40,7 +39,6 @@
#[cfg(test)]
mod tests {
- use super::is_nan;
use crate::prelude::*;
#[test]
diff --git a/crates/googletest/src/matchers/le_matcher.rs b/crates/googletest/src/matchers/le_matcher.rs
index cfc5781..980c516 100644
--- a/crates/googletest/src/matchers/le_matcher.rs
+++ b/crates/googletest/src/matchers/le_matcher.rs
@@ -14,9 +14,9 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches a value less than or equal to (in the sense of `<=`) `expected`.
///
@@ -74,24 +74,20 @@
///
/// You can find the standard library `PartialOrd` implementation in
/// <https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#implementors>
-pub fn le<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug>(
- expected: ExpectedT,
-) -> impl Matcher<ActualT = ActualT> {
- LeMatcher::<ActualT, _> { expected, phantom: Default::default() }
+pub fn le<ExpectedT>(expected: ExpectedT) -> LeMatcher<ExpectedT> {
+ LeMatcher { expected }
}
-struct LeMatcher<ActualT, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct LeMatcher<ExpectedT> {
expected: ExpectedT,
- phantom: PhantomData<ActualT>,
}
-impl<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug> Matcher
- for LeMatcher<ActualT, ExpectedT>
+impl<ActualT: Debug + PartialOrd<ExpectedT> + Copy, ExpectedT: Debug> Matcher<ActualT>
+ for LeMatcher<ExpectedT>
{
- type ActualT = ActualT;
-
- fn matches(&self, actual: &ActualT) -> MatcherResult {
- (*actual <= self.expected).into()
+ fn matches(&self, actual: ActualT) -> MatcherResult {
+ (actual <= self.expected).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -104,8 +100,7 @@
#[cfg(test)]
mod tests {
- use super::le;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
use std::ffi::OsString;
@@ -120,7 +115,7 @@
#[test]
fn le_does_not_match_bigger_i32() -> Result<()> {
let matcher = le(0);
- let result = matcher.matches(&1);
+ let result = matcher.matches(1);
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -132,7 +127,7 @@
#[test]
fn le_does_not_match_bigger_str() -> Result<()> {
let matcher = le("a");
- let result = matcher.matches(&"z");
+ let result = matcher.matches("z");
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -186,7 +181,7 @@
/// A custom "number" that is lower than all other numbers. The only
/// things we define about this "special" number is `PartialOrd` and
/// `PartialEq` against `u32`.
- #[derive(Debug)]
+ #[derive(Debug, Clone, Copy)]
struct VeryLowNumber {}
impl std::cmp::PartialEq<u32> for VeryLowNumber {
diff --git a/crates/googletest/src/matchers/len_matcher.rs b/crates/googletest/src/matchers/len_matcher.rs
index be903c9..a48d32c 100644
--- a/crates/googletest/src/matchers/len_matcher.rs
+++ b/crates/googletest/src/matchers/len_matcher.rs
@@ -13,16 +13,15 @@
// limitations under the License.
use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use crate::matcher_support::count_elements::count_elements;
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches a container whose number of elements matches `expected`.
///
/// This matches against a container over which one can iterate. This includes
-/// the standard Rust containers, arrays, and (when dereferenced) slices. More
-/// precisely, a shared borrow of the actual type must implement
-/// [`IntoIterator`].
+/// the standard Rust containers, arrays, and slices. More
+/// precisely, the actual type must implement [`IntoIterator`].
///
/// ```
/// # use googletest::prelude::*;
@@ -49,26 +48,21 @@
/// # }
/// # should_pass().unwrap();
/// ```
-pub fn len<T: Debug + ?Sized, E: Matcher<ActualT = usize>>(expected: E) -> impl Matcher<ActualT = T>
-where
- for<'a> &'a T: IntoIterator,
-{
- LenMatcher { expected, phantom: Default::default() }
+pub fn len<E>(expected: E) -> LenMatcher<E> {
+ LenMatcher { expected }
}
-struct LenMatcher<T: ?Sized, E> {
+#[derive(MatcherBase)]
+pub struct LenMatcher<E> {
expected: E,
- phantom: PhantomData<T>,
}
-impl<T: Debug + ?Sized, E: Matcher<ActualT = usize>> Matcher for LenMatcher<T, E>
+impl<T: Debug + Copy, E: Matcher<usize>> Matcher<T> for LenMatcher<E>
where
- for<'a> &'a T: IntoIterator,
+ T: IntoIterator,
{
- type ActualT = T;
-
- fn matches(&self, actual: &T) -> MatcherResult {
- self.expected.matches(&count_elements(actual))
+ fn matches(&self, actual: T) -> MatcherResult {
+ self.expected.matches(count_elements(actual))
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -83,25 +77,23 @@
}
}
- fn explain_match(&self, actual: &T) -> Description {
+ fn explain_match(&self, actual: T) -> Description {
let actual_size = count_elements(actual);
- format!("which has length {}, {}", actual_size, self.expected.explain_match(&actual_size))
+ format!("which has length {}, {}", actual_size, self.expected.explain_match(actual_size))
.into()
}
}
#[cfg(test)]
mod tests {
- use super::len;
use crate::description::Description;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
use std::collections::{
BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque,
};
use std::fmt::Debug;
- use std::marker::PhantomData;
#[test]
fn len_matcher_match_vec() -> Result<()> {
@@ -112,7 +104,7 @@
#[test]
fn len_matcher_match_array_reference() -> Result<()> {
let value = &[1, 2, 3];
- verify_that!(*value, len(eq(3)))
+ verify_that!(value, len(eq(3)))
}
#[test]
@@ -125,7 +117,7 @@
fn len_matcher_match_slice_of_vec() -> Result<()> {
let value = vec![1, 2, 3];
let slice = value.as_slice();
- verify_that!(*slice, len(eq(3)))
+ verify_that!(slice, len(eq(3)))
}
#[test]
@@ -178,11 +170,10 @@
#[test]
fn len_matcher_explain_match() -> Result<()> {
- struct TestMatcher<T>(PhantomData<T>);
- impl<T: Debug> Matcher for TestMatcher<T> {
- type ActualT = T;
-
- fn matches(&self, _: &T) -> MatcherResult {
+ #[derive(MatcherBase)]
+ struct TestMatcher;
+ impl<T: Debug + Copy> Matcher<T> for TestMatcher {
+ fn matches(&self, _: T) -> MatcherResult {
false.into()
}
@@ -190,12 +181,12 @@
"called described".into()
}
- fn explain_match(&self, _: &T) -> Description {
+ fn explain_match(&self, _: T) -> Description {
"called explain_match".into()
}
}
verify_that!(
- len(TestMatcher(Default::default())).explain_match(&[1, 2, 3]),
+ len(TestMatcher).explain_match([1, 2, 3]),
displays_as(eq("which has length 3, called explain_match"))
)
}
diff --git a/crates/googletest/src/matchers/lt_matcher.rs b/crates/googletest/src/matchers/lt_matcher.rs
index 08bc563..1758e83 100644
--- a/crates/googletest/src/matchers/lt_matcher.rs
+++ b/crates/googletest/src/matchers/lt_matcher.rs
@@ -14,9 +14,9 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches a value less (in the sense of `<`) than `expected`.
///
@@ -74,24 +74,20 @@
///
/// You can find the standard library `PartialOrd` implementation in
/// <https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html#implementors>
-pub fn lt<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug>(
- expected: ExpectedT,
-) -> impl Matcher<ActualT = ActualT> {
- LtMatcher::<ActualT, _> { expected, phantom: Default::default() }
+pub fn lt<ExpectedT>(expected: ExpectedT) -> LtMatcher<ExpectedT> {
+ LtMatcher { expected }
}
-struct LtMatcher<ActualT, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct LtMatcher<ExpectedT> {
expected: ExpectedT,
- phantom: PhantomData<ActualT>,
}
-impl<ActualT: Debug + PartialOrd<ExpectedT>, ExpectedT: Debug> Matcher
- for LtMatcher<ActualT, ExpectedT>
+impl<ActualT: Debug + PartialOrd<ExpectedT> + Copy, ExpectedT: Debug> Matcher<ActualT>
+ for LtMatcher<ExpectedT>
{
- type ActualT = ActualT;
-
- fn matches(&self, actual: &ActualT) -> MatcherResult {
- (*actual < self.expected).into()
+ fn matches(&self, actual: ActualT) -> MatcherResult {
+ (actual < self.expected).into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -106,8 +102,7 @@
#[cfg(test)]
mod tests {
- use super::lt;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
use std::ffi::OsString;
@@ -122,14 +117,14 @@
#[test]
fn lt_does_not_match_equal_i32() -> Result<()> {
let matcher = lt(10);
- let result = matcher.matches(&10);
+ let result = matcher.matches(10);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn lt_does_not_match_lower_i32() -> Result<()> {
let matcher = lt(-50);
- let result = matcher.matches(&50);
+ let result = matcher.matches(50);
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -141,7 +136,7 @@
#[test]
fn lt_does_not_match_bigger_str() -> Result<()> {
let matcher = lt("ab");
- let result = matcher.matches(&"az");
+ let result = matcher.matches("az");
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -195,7 +190,7 @@
/// A custom "number" that is smaller than all other numbers. The only
/// things we define about this "special" number is `PartialOrd` and
/// `PartialEq` against `u32`.
- #[derive(Debug)]
+ #[derive(Debug, Clone, Copy)]
struct VeryLowNumber {}
impl std::cmp::PartialEq<u32> for VeryLowNumber {
diff --git a/crates/googletest/src/matchers/matches_pattern.rs b/crates/googletest/src/matchers/matches_pattern.rs
index 106a5d7..7dc9ce3 100644
--- a/crates/googletest/src/matchers/matches_pattern.rs
+++ b/crates/googletest/src/matchers/matches_pattern.rs
@@ -84,7 +84,7 @@
/// a_nested_struct: MyInnerStruct { a_field: "Something to believe in".into() },
/// };
/// verify_that!(my_struct, matches_pattern!(MyStruct {
-/// a_nested_struct: matches_pattern!(MyInnerStruct {
+/// a_nested_struct: matches_pattern!(MyInnerStruct {
/// a_field: starts_with("Something"),
/// }),
/// }))
@@ -138,12 +138,33 @@
/// # .unwrap();
/// ```
///
+/// If an inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// struct MyStruct {
+/// a_field: String,
+/// another_field: String,
+/// }
+///
+/// let my_struct = MyStruct {
+/// a_field: "this".into(),
+/// another_field: "that".into()
+/// };
+/// verify_that!(my_struct, matches_pattern!(MyStruct {
+/// a_field: "this",
+/// another_field: "that",
+/// }))
+/// # .unwrap();
+/// ```
+///
/// **Important**: The method should be pure function with a deterministic
/// output and no side effects. In particular, in the event of an assertion
/// failure, it will be invoked a second time, with the assertion failure output
/// reflecting the *second* invocation.
///
-/// These may also include extra parameters you pass in:
+/// These may also include extra litteral parameters you pass in:
///
/// ```
/// # use googletest::prelude::*;
@@ -157,13 +178,14 @@
/// }
///
/// # let my_struct = MyStruct { a_field: "Something to believe in".into() };
-/// verify_that!(my_struct, matches_pattern!(MyStruct {
-/// append_to_a_field("a suffix"): ends_with("a suffix"),
+/// verify_that!(my_struct, matches_pattern!(&MyStruct {
+/// append_to_a_field("a suffix"): ref ends_with("a suffix"),
/// }))
/// # .unwrap();
/// ```
///
-/// If the method returns a reference, precede it with a `*`:
+/// You can precede both field and property matchers with a `ref` to match the
+/// result by reference:
///
/// ```
/// # use googletest::prelude::*;
@@ -173,12 +195,34 @@
/// # }
/// #
/// impl MyStruct {
-/// fn get_a_field_ref(&self) -> &String { &self.a_field }
+/// fn get_a_field_ref(&self) -> String { self.a_field.clone() }
+/// }
+///
+/// # let my_struct = MyStruct { a_field: "Something to believe in".into() };
+/// verify_that!(my_struct, matches_pattern!(&MyStruct {
+/// get_a_field_ref(): ref starts_with("Something"),
+/// }))
+/// # .unwrap();
+/// ```
+///
+/// Note that if the `actual` is of type `&ActualT` and the pattern type is
+/// `ActualT`, this is automatically performed. This behavior is similar to the
+/// reference binding mode in pattern matching.
+///
+/// ```
+/// # use googletest::prelude::*;
+/// # #[derive(Debug)]
+/// # struct MyStruct {
+/// # a_field: String,
+/// # }
+/// #
+/// impl MyStruct {
+/// fn get_a_field_ref(&self) -> String { self.a_field.clone() }
/// }
///
/// # let my_struct = MyStruct { a_field: "Something to believe in".into() };
/// verify_that!(my_struct, matches_pattern!(MyStruct {
-/// *get_a_field_ref(): starts_with("Something"),
+/// get_a_field_ref(): starts_with("Something"),
/// }))
/// # .unwrap();
/// ```
@@ -194,7 +238,7 @@
/// let my_struct = MyTupleStruct("Something".into(), "Some other thing".into());
/// verify_that!(
/// my_struct,
-/// matches_pattern!(MyTupleStruct(eq("Something"), eq("Some other thing")))
+/// matches_pattern!(&MyTupleStruct(ref eq("Something"), ref eq("Some other thing")))
/// )
/// # .unwrap();
/// ```
@@ -210,42 +254,34 @@
/// }
///
/// # fn should_pass() -> Result<()> {
-/// verify_that!(MyEnum::A(123), matches_pattern!(MyEnum::A(eq(123))))?; // Passes
+/// verify_that!(MyEnum::A(123), matches_pattern!(&MyEnum::A(eq(123))))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
-/// verify_that!(MyEnum::B, matches_pattern!(MyEnum::A(eq(123))))?; // Fails - wrong enum variant
+/// verify_that!(MyEnum::B, matches_pattern!(&MyEnum::A(eq(123))))?; // Fails - wrong enum variant
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
///
-/// This macro does not support plain (non-struct) tuples. Use the macro
-/// [`tuple`] for that purpose.
+/// This macro does not support plain (non-struct) tuples. But it should not be
+/// necessary as tuple of matchers are matchers of tuple. In other words, if
+/// `MatcherU: Matcher<U>` and `MatcherT: Matcher<T>`, then `(MatcherU,
+/// MatcherT): Matcher<(U, T)>`.
///
/// Trailing commas are allowed (but not required) in both ordinary and tuple
/// structs.
///
-/// Unfortunately, this matcher does *not* work with methods returning string
-/// slices:
+/// Note that the default format (rustfmt) can format macros if the macro
+/// argument is parseable Rust code. This is mostly true for this macro with two
+/// exceptions:
+/// * property matching
+/// * `ref` keyword with named fields
///
-/// ```compile_fail
-/// # use googletest::prelude::*;
-/// # #[derive(Debug)]
-/// pub struct MyStruct {
-/// a_string: String,
-/// }
-/// impl MyStruct {
-/// pub fn get_a_string(&self) -> &str { &self.a_string }
-/// }
-///
-/// let value = MyStruct { a_string: "A string".into() };
-/// verify_that!(value, matches_pattern!( MyStruct {
-/// get_a_string(): eq("A string"), // Does not compile
-/// }))
-/// # .unwrap();
-/// ```
+/// An option for formatting large is to avoid these exceptions (by removing the
+/// parenthesis of properties and the `ref` keywords), run `rustfmt` and add
+/// them back.
#[macro_export]
#[doc(hidden)]
macro_rules! __matches_pattern {
@@ -258,7 +294,17 @@
#[macro_export]
macro_rules! matches_pattern_internal {
(
- [$($struct_name:tt)*],
+ @name [$($struct_name:tt)*],
+ { $field_name:ident : ref $matcher:expr $(,)? }
+ ) => {
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+ stringify!($($struct_name)*),
+ all!(field!($($struct_name)*.$field_name, ref $matcher))
+ )
+ };
+
+ (
+ @name [$($struct_name:tt)*],
{ $field_name:ident : $matcher:expr $(,)? }
) => {
$crate::matchers::__internal_unstable_do_not_depend_on_these::is(
@@ -268,7 +314,17 @@
};
(
- [$($struct_name:tt)*],
+ @name [$($struct_name:tt)*],
+ { $property_name:ident($($argument:expr),* $(,)?) : ref $matcher:expr $(,)? }
+ ) => {
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+ stringify!($($struct_name)*),
+ all!(property!($($struct_name)*.$property_name($($argument),*), ref $matcher))
+ )
+ };
+
+ (
+ @name [$($struct_name:tt)*],
{ $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
) => {
$crate::matchers::__internal_unstable_do_not_depend_on_these::is(
@@ -278,88 +334,123 @@
};
(
- [$($struct_name:tt)*],
- { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
- ) => {
- $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
- stringify!($($struct_name)*),
- all!(property!(* $($struct_name)*.$property_name($($argument),*), $matcher))
- )
- };
-
- (
- [$($struct_name:tt)*],
- { $field_name:ident : $matcher:expr, $($rest:tt)* }
+ @name [$($struct_name:tt)*],
+ { $field_name:ident : ref $matcher:expr, $($rest:tt)* }
) => {
$crate::matches_pattern_internal!(
- all!(field!($($struct_name)*.$field_name, $matcher)),
+ @fields (field!($($struct_name)*.$field_name, ref $matcher)),
[$($struct_name)*],
{ $($rest)* }
)
};
(
- [$($struct_name:tt)*],
+ @name [$($struct_name:tt)*],
+ { $field_name:ident : $matcher:expr, $($rest:tt)* }
+ ) => {
+ $crate::matches_pattern_internal!(
+ @fields (field!($($struct_name)*.$field_name, $matcher)),
+ [$($struct_name)*],
+ { $($rest)* }
+ )
+ };
+
+ (
+ @name [$($struct_name:tt)*],
+ { $property_name:ident($($argument:expr),* $(,)?) : ref $matcher:expr, $($rest:tt)* }
+ ) => {
+ $crate::matches_pattern_internal!(
+ @fields (property!($($struct_name)*.$property_name($($argument),*), ref $matcher)),
+ [$($struct_name)*],
+ { $($rest)* }
+ )
+ };
+
+ (
+ @name [$($struct_name:tt)*],
{ $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* }
) => {
$crate::matches_pattern_internal!(
- all!(property!($($struct_name)*.$property_name($($argument),*), $matcher)),
+ @fields (property!($($struct_name)*.$property_name($($argument),*), $matcher)),
[$($struct_name)*],
{ $($rest)* }
)
};
(
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
- { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* }
+ { $field_name:ident : ref $matcher:expr $(,)? }
) => {
- $crate::matches_pattern_internal!(
- all!(property!(* $($struct_name)*.$property_name($($argument),*), $matcher)),
- [$($struct_name)*],
- { $($rest)* }
- )
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+ stringify!($($struct_name)*),
+ all!(
+ $($processed)*,
+ field!($($struct_name)*.$field_name, ref $matcher)
+ ))
};
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
{ $field_name:ident : $matcher:expr $(,)? }
) => {
- $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!(
- $($processed)*,
- field!($($struct_name)*.$field_name, $matcher)
- ))
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+ stringify!($($struct_name)*),
+ all!(
+ $($processed)*,
+ field!($($struct_name)*.$field_name, $matcher)
+ ))
};
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
+ [$($struct_name:tt)*],
+ { $property_name:ident($($argument:expr),* $(,)?) : ref $matcher:expr $(,)? }
+ ) => {
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+ stringify!($($struct_name)*),
+ all!(
+ $($processed)*,
+ property!($($struct_name)*.$property_name($($argument),*), ref $matcher)
+ ))
+ };
+
+ (
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
{ $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
) => {
- $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!(
- $($processed)*,
- property!($($struct_name)*.$property_name($($argument),*), $matcher)
- ))
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+ stringify!($($struct_name)*),
+ all!(
+ $($processed)*,
+ property!($($struct_name)*.$property_name($($argument),*), $matcher)
+ ))
};
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
- { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr $(,)? }
+ { $field_name:ident : ref $matcher:expr, $($rest:tt)* }
) => {
- $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!(
- $($processed)*,
- property!(* $($struct_name)*.$property_name($($argument),*), $matcher)
- ))
+ $crate::matches_pattern_internal!(
+ @fields (
+ $($processed)*,
+ field!($($struct_name)*.$field_name, ref $matcher)
+ ),
+ [$($struct_name)*],
+ { $($rest)* }
+ )
};
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
{ $field_name:ident : $matcher:expr, $($rest:tt)* }
) => {
$crate::matches_pattern_internal!(
- all!(
+ @fields (
$($processed)*,
field!($($struct_name)*.$field_name, $matcher)
),
@@ -369,12 +460,27 @@
};
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
+ [$($struct_name:tt)*],
+ { $property_name:ident($($argument:expr),* $(,)?) : ref $matcher:expr, $($rest:tt)* }
+ ) => {
+ $crate::matches_pattern_internal!(
+ @fields (
+ $($processed)*,
+ property!(ref $($struct_name)*.$property_name($($argument),*), $matcher)
+ ),
+ [$($struct_name)*],
+ { $($rest)* }
+ )
+ };
+
+ (
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
{ $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* }
) => {
$crate::matches_pattern_internal!(
- all!(
+ @fields (
$($processed)*,
property!($($struct_name)*.$property_name($($argument),*), $matcher)
),
@@ -384,32 +490,26 @@
};
(
- all!($($processed:tt)*),
- [$($struct_name:tt)*],
- { * $property_name:ident($($argument:expr),* $(,)?) : $matcher:expr, $($rest:tt)* }
+ @name [$($struct_name:tt)*],
) => {
- $crate::matches_pattern_internal!(
- all!(
- $($processed)*,
- property!(* $($struct_name)*.$property_name($($argument),*), $matcher)
- ),
- [$($struct_name)*],
- { $($rest)* }
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::pattern_only(
+ |v| matches!(v, $($struct_name)*),
+ concat!("is ", stringify!($($struct_name)*)),
+ concat!("is not ", stringify!($($struct_name)*)))
+ };
+
+ (
+ @name [$($struct_name:tt)*],
+ (ref $matcher:expr $(,)?)
+ ) => {
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+ stringify!($($struct_name)*),
+ all!(field!($($struct_name)*.0, ref $matcher))
)
};
(
- [$($struct_name:tt)*],
- ) => {
- $crate::matchers::predicate(|v| matches!(v, $($struct_name)*))
- .with_description(
- concat!("is ", stringify!($($struct_name)*)),
- concat!("is not ", stringify!($($struct_name)*)),
- )
- };
-
- (
- [$($struct_name:tt)*],
+ @name [$($struct_name:tt)*],
($matcher:expr $(,)?)
) => {
$crate::matchers::__internal_unstable_do_not_depend_on_these::is(
@@ -419,11 +519,25 @@
};
(
- [$($struct_name:tt)*],
+ @name [$($struct_name:tt)*],
+ (ref $matcher:expr, $($rest:tt)*)
+ ) => {
+ $crate::matches_pattern_internal!(
+ @fields (
+ field!($($struct_name)*.0, ref $matcher)
+ ),
+ [$($struct_name)*],
+ 1,
+ ($($rest)*)
+ )
+ };
+
+ (
+ @name [$($struct_name:tt)*],
($matcher:expr, $($rest:tt)*)
) => {
$crate::matches_pattern_internal!(
- all!(
+ @fields (
field!($($struct_name)*.0, $matcher)
),
[$($struct_name)*],
@@ -433,28 +547,61 @@
};
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
+ [$($struct_name:tt)*],
+ $field:tt,
+ (ref $matcher:expr $(,)?)
+ ) => {
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+ stringify!($($struct_name)*),
+ all!(
+ $($processed)*,
+ field!($($struct_name)*.$field, ref $matcher)
+ ))
+ };
+
+ (
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
$field:tt,
($matcher:expr $(,)?)
) => {
- $crate::matchers::__internal_unstable_do_not_depend_on_these::is(stringify!($($struct_name)*), all!(
- $($processed)*,
- field!($($struct_name)*.$field, $matcher)
- ))
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::is(
+ stringify!($($struct_name)*),
+ all!(
+ $($processed)*,
+ field!($($struct_name)*.$field, $matcher)
+ ))
};
// We need to repeat this once for every supported field position, unfortunately. There appears
// to be no way in declarative macros to compute $field + 1 and have the result evaluated to a
// token which can be used as a tuple index.
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
+ [$($struct_name:tt)*],
+ 1,
+ (ref $matcher:expr, $($rest:tt)*)
+ ) => {
+ $crate::matches_pattern_internal!(
+ @fields (
+ $($processed)*,
+ field!($($struct_name)*.1, ref $matcher)
+ ),
+ [$($struct_name)*],
+ 2,
+ ($($rest)*)
+ )
+ };
+
+ (
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
1,
($matcher:expr, $($rest:tt)*)
) => {
$crate::matches_pattern_internal!(
- all!(
+ @fields (
$($processed)*,
field!($($struct_name)*.1, $matcher)
),
@@ -465,13 +612,30 @@
};
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
+ [$($struct_name:tt)*],
+ 2,
+ (ref $matcher:expr, $($rest:tt)*)
+ ) => {
+ $crate::matches_pattern_internal!(
+ @fields (
+ $($processed)*,
+ field!($($struct_name)*.2, ref $matcher)
+ ),
+ [$($struct_name)*],
+ 3,
+ ($($rest)*)
+ )
+ };
+
+ (
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
2,
($matcher:expr, $($rest:tt)*)
) => {
$crate::matches_pattern_internal!(
- all!(
+ @fields (
$($processed)*,
field!($($struct_name)*.2, $matcher)
),
@@ -482,13 +646,30 @@
};
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
+ [$($struct_name:tt)*],
+ 3,
+ (ref $matcher:expr, $($rest:tt)*)
+ ) => {
+ $crate::matches_pattern_internal!(
+ @fields (
+ $($processed)*,
+ field!($($struct_name)*.3, ref $matcher)
+ ),
+ [$($struct_name)*],
+ 4,
+ ($($rest)*)
+ )
+ };
+
+ (
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
3,
($matcher:expr, $($rest:tt)*)
) => {
$crate::matches_pattern_internal!(
- all!(
+ @fields (
$($processed)*,
field!($($struct_name)*.3, $matcher)
),
@@ -499,13 +680,30 @@
};
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
+ [$($struct_name:tt)*],
+ 4,
+ (ref $matcher:expr, $($rest:tt)*)
+ ) => {
+ $crate::matches_pattern_internal!(
+ @fields (
+ $($processed)*,
+ field!($($struct_name)*.4, ref $matcher)
+ ),
+ [$($struct_name)*],
+ 5,
+ ($($rest)*)
+ )
+ };
+
+ (
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
4,
($matcher:expr, $($rest:tt)*)
) => {
$crate::matches_pattern_internal!(
- all!(
+ @fields (
$($processed)*,
field!($($struct_name)*.4, $matcher)
),
@@ -516,13 +714,30 @@
};
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
+ [$($struct_name:tt)*],
+ 5,
+ (ref $matcher:expr, $($rest:tt)*)
+ ) => {
+ $crate::matches_pattern_internal!(
+ @fields (
+ $($processed)*,
+ field!($($struct_name)*.5, ref $matcher)
+ ),
+ [$($struct_name)*],
+ 6,
+ ($($rest)*)
+ )
+ };
+
+ (
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
5,
($matcher:expr, $($rest:tt)*)
) => {
$crate::matches_pattern_internal!(
- all!(
+ @fields (
$($processed)*,
field!($($struct_name)*.5, $matcher)
),
@@ -533,13 +748,30 @@
};
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
+ [$($struct_name:tt)*],
+ 6,
+ (ref $matcher:expr, $($rest:tt)*)
+ ) => {
+ $crate::matches_pattern_internal!(
+ @fields (
+ $($processed)*,
+ field!($($struct_name)*.6, ref $matcher)
+ ),
+ [$($struct_name)*],
+ 7,
+ ($($rest)*)
+ )
+ };
+
+ (
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
6,
($matcher:expr, $($rest:tt)*)
) => {
$crate::matches_pattern_internal!(
- all!(
+ @fields (
$($processed)*,
field!($($struct_name)*.6, $matcher)
),
@@ -550,13 +782,30 @@
};
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
+ [$($struct_name:tt)*],
+ 7,
+ (ref $matcher:expr, $($rest:tt)*)
+ ) => {
+ $crate::matches_pattern_internal!(
+ @fields (
+ $($processed)*,
+ field!($($struct_name)*.7, ref $matcher)
+ ),
+ [$($struct_name)*],
+ 8,
+ ($($rest)*)
+ )
+ };
+
+ (
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
7,
($matcher:expr, $($rest:tt)*)
) => {
$crate::matches_pattern_internal!(
- all!(
+ @fields (
$($processed)*,
field!($($struct_name)*.7, $matcher)
),
@@ -567,13 +816,30 @@
};
(
- all!($($processed:tt)*),
+ @fields ($($processed:tt)*),
+ [$($struct_name:tt)*],
+ 8,
+ (ref $matcher:expr, $($rest:tt)*)
+ ) => {
+ $crate::matches_pattern_internal!(
+ @fields (
+ $($processed)*,
+ field!($($struct_name)*.8, ref $matcher)
+ ),
+ [$($struct_name)*],
+ 9,
+ ($($rest)*)
+ )
+ };
+
+ (
+ @fields ($($processed:tt)*),
[$($struct_name:tt)*],
8,
($matcher:expr, $($rest:tt)*)
) => {
$crate::matches_pattern_internal!(
- all!(
+ @fields (
$($processed)*,
field!($($struct_name)*.8, $matcher)
),
@@ -583,14 +849,14 @@
)
};
- ([$($struct_name:tt)*], $first:tt $($rest:tt)*) => {
- $crate::matches_pattern_internal!([$($struct_name)* $first], $($rest)*)
+ (@name [$($struct_name:tt)*], $first:tt $($rest:tt)*) => {
+ $crate::matches_pattern_internal!(@name [$($struct_name)* $first], $($rest)*)
};
($first:tt $($rest:tt)*) => {{
#[allow(unused)]
use $crate::matchers::{all, field, property};
- $crate::matches_pattern_internal!([$first], $($rest)*)
+ $crate::matches_pattern_internal!(@name [$first], $($rest)*)
}};
}
@@ -600,3 +866,59 @@
macro_rules! __pat {
($($t:tt)*) => { $crate::matches_pattern_internal!($($t)*) }
}
+
+#[doc(hidden)]
+pub mod internal {
+ use crate::matcher::{Matcher, MatcherBase};
+ use std::fmt::Debug;
+
+ // Specialized implementation of the `predicate` matcher to support ref binding
+ // mode for `matches_pattern`.
+ pub fn pattern_only<T>(
+ matcher_function: fn(&T) -> bool,
+ match_description: &'static str,
+ no_match_description: &'static str,
+ ) -> PatternOnlyMatcher<T> {
+ PatternOnlyMatcher { matcher_function, match_description, no_match_description }
+ }
+
+ #[derive(MatcherBase)]
+ #[doc(hidden)]
+ pub struct PatternOnlyMatcher<T> {
+ matcher_function: fn(&T) -> bool,
+ match_description: &'static str,
+ no_match_description: &'static str,
+ }
+
+ impl<'a, T: Debug> Matcher<&'a T> for PatternOnlyMatcher<T> {
+ fn matches(&self, actual: &'a T) -> crate::matcher::MatcherResult {
+ (self.matcher_function)(actual).into()
+ }
+
+ fn describe(
+ &self,
+ matcher_result: crate::matcher::MatcherResult,
+ ) -> crate::description::Description {
+ match matcher_result {
+ crate::matcher::MatcherResult::Match => self.match_description.into(),
+ crate::matcher::MatcherResult::NoMatch => self.no_match_description.into(),
+ }
+ }
+ }
+
+ impl<T: Debug + Copy> Matcher<T> for PatternOnlyMatcher<T> {
+ fn matches(&self, actual: T) -> crate::matcher::MatcherResult {
+ (self.matcher_function)(&actual).into()
+ }
+
+ fn describe(
+ &self,
+ matcher_result: crate::matcher::MatcherResult,
+ ) -> crate::description::Description {
+ match matcher_result {
+ crate::matcher::MatcherResult::Match => self.match_description.into(),
+ crate::matcher::MatcherResult::NoMatch => self.no_match_description.into(),
+ }
+ }
+ }
+}
diff --git a/crates/googletest/src/matchers/matches_regex_matcher.rs b/crates/googletest/src/matchers/matches_regex_matcher.rs
index 32b053b..2e00fb6 100644
--- a/crates/googletest/src/matchers/matches_regex_matcher.rs
+++ b/crates/googletest/src/matchers/matches_regex_matcher.rs
@@ -13,10 +13,9 @@
// limitations under the License.
use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use regex::Regex;
use std::fmt::Debug;
-use std::marker::PhantomData;
use std::ops::Deref;
/// Matches a string the entirety of which which matches the given regular
@@ -60,38 +59,31 @@
// compiler treats it as a Matcher<str> only and the code
// verify_that!("Some value".to_string(), matches_regex(".*value"))?;
// doesn't compile.
-pub fn matches_regex<ActualT: ?Sized, PatternT: Deref<Target = str>>(
+pub fn matches_regex<PatternT: Deref<Target = str>>(
pattern: PatternT,
-) -> MatchesRegexMatcher<ActualT, PatternT> {
+) -> MatchesRegexMatcher<PatternT> {
let adjusted_pattern = format!("^{}$", pattern.deref());
let regex = Regex::new(adjusted_pattern.as_str()).unwrap();
- MatchesRegexMatcher {
- regex,
- pattern,
- _adjusted_pattern: adjusted_pattern,
- phantom: Default::default(),
- }
+ MatchesRegexMatcher { regex, pattern, _adjusted_pattern: adjusted_pattern }
}
/// A matcher matching a string-like type matching a given regular expression.
///
/// Intended only to be used from the function [`matches_regex`] only.
/// Should not be referenced by code outside this library.
-pub struct MatchesRegexMatcher<ActualT: ?Sized, PatternT: Deref<Target = str>> {
+#[derive(MatcherBase)]
+pub struct MatchesRegexMatcher<PatternT: Deref<Target = str>> {
regex: Regex,
pattern: PatternT,
_adjusted_pattern: String,
- phantom: PhantomData<ActualT>,
}
-impl<PatternT, ActualT> Matcher for MatchesRegexMatcher<ActualT, PatternT>
+impl<PatternT, ActualT> Matcher<ActualT> for MatchesRegexMatcher<PatternT>
where
PatternT: Deref<Target = str>,
- ActualT: AsRef<str> + Debug + ?Sized,
+ ActualT: AsRef<str> + Debug + Copy,
{
- type ActualT = ActualT;
-
- fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+ fn matches(&self, actual: ActualT) -> MatcherResult {
self.regex.is_match(actual.as_ref()).into()
}
@@ -109,8 +101,7 @@
#[cfg(test)]
mod tests {
- use super::{matches_regex, MatchesRegexMatcher};
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
#[test]
@@ -201,10 +192,10 @@
#[test]
fn matches_regex_displays_quoted_debug_of_pattern() -> Result<()> {
- let matcher: MatchesRegexMatcher<&str, _> = matches_regex("\n");
+ let matcher = matches_regex("\n");
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("matches the regular expression \"\\n\""))
)
}
diff --git a/crates/googletest/src/matchers/mod.rs b/crates/googletest/src/matchers/mod.rs
index 1e028b9..16ca900 100644
--- a/crates/googletest/src/matchers/mod.rs
+++ b/crates/googletest/src/matchers/mod.rs
@@ -17,17 +17,18 @@
mod all_matcher;
mod any_matcher;
mod anything_matcher;
+mod bool_matcher;
mod char_count_matcher;
mod conjunction_matcher;
mod container_eq_matcher;
mod contains_matcher;
mod contains_regex_matcher;
+mod derefs_to_matcher;
mod disjunction_matcher;
mod display_matcher;
mod each_matcher;
mod elements_are_matcher;
mod empty_matcher;
-mod eq_deref_of_matcher;
mod eq_matcher;
mod err_matcher;
mod field_matcher;
@@ -50,6 +51,7 @@
mod pointwise_matcher;
mod predicate_matcher;
mod property_matcher;
+mod result_of_matcher;
mod some_matcher;
mod str_matcher;
mod subset_of_matcher;
@@ -58,14 +60,15 @@
mod unordered_elements_are_matcher;
pub use anything_matcher::anything;
+pub use bool_matcher::{is_false, is_true};
pub use char_count_matcher::char_count;
pub use container_eq_matcher::container_eq;
pub use contains_matcher::{contains, ContainsMatcher};
pub use contains_regex_matcher::contains_regex;
+pub use derefs_to_matcher::derefs_to;
pub use display_matcher::displays_as;
pub use each_matcher::each;
pub use empty_matcher::empty;
-pub use eq_deref_of_matcher::eq_deref_of;
pub use eq_matcher::{eq, EqMatcher};
pub use err_matcher::err;
pub use ge_matcher::ge;
@@ -95,8 +98,8 @@
pub use crate::{
__all as all, __any as any, __contains_each as contains_each, __elements_are as elements_are,
__field as field, __is_contained_in as is_contained_in, __matches_pattern as matches_pattern,
- __pat as pat, __pointwise as pointwise, __property as property,
- __unordered_elements_are as unordered_elements_are,
+ __pat as pat, __pointwise as pointwise, __property as property, __result_of as result_of,
+ __result_of_ref as result_of_ref, __unordered_elements_are as unordered_elements_are,
};
// Types and functions used by macros matchers.
@@ -105,16 +108,16 @@
// should only be used through their respective macros.
#[doc(hidden)]
pub mod __internal_unstable_do_not_depend_on_these {
- pub use super::all_matcher::internal::AllMatcher;
- pub use super::any_matcher::internal::AnyMatcher;
pub use super::conjunction_matcher::ConjunctionMatcher;
pub use super::disjunction_matcher::DisjunctionMatcher;
pub use super::elements_are_matcher::internal::ElementsAre;
pub use super::field_matcher::internal::field_matcher;
pub use super::is_matcher::is;
+ pub use super::matches_pattern::internal::pattern_only;
pub use super::pointwise_matcher::internal::PointwiseMatcher;
pub use super::property_matcher::internal::{property_matcher, property_ref_matcher};
+ pub use super::result_of_matcher::internal::{result_of, result_of_ref};
pub use super::unordered_elements_are_matcher::internal::{
- Requirements, UnorderedElementsAreMatcher, UnorderedElementsOfMapAreMatcher,
+ Requirements, UnorderedElementsAreMatcher,
};
}
diff --git a/crates/googletest/src/matchers/near_matcher.rs b/crates/googletest/src/matchers/near_matcher.rs
index ca7cbdf..de1eb3d 100644
--- a/crates/googletest/src/matchers/near_matcher.rs
+++ b/crates/googletest/src/matchers/near_matcher.rs
@@ -14,7 +14,7 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
use num_traits::{Float, FloatConst};
use std::fmt::Debug;
@@ -110,6 +110,7 @@
/// # }
/// # should_pass().unwrap();
/// ```
+#[track_caller]
pub fn near<T: Debug + Float + Copy>(expected: T, max_abs_error: T) -> NearMatcher<T> {
if max_abs_error.is_nan() {
panic!("max_abs_error must not be NaN");
@@ -139,6 +140,7 @@
/// A matcher which matches floating-point numbers approximately equal to its
/// expected value.
+#[derive(MatcherBase)]
pub struct NearMatcher<T: Debug> {
expected: T,
max_abs_error: T,
@@ -166,15 +168,13 @@
}
}
-impl<T: Debug + Float> Matcher for NearMatcher<T> {
- type ActualT = T;
-
- fn matches(&self, actual: &T) -> MatcherResult {
+impl<T: Debug + Float + Copy> Matcher<T> for NearMatcher<T> {
+ fn matches(&self, actual: T) -> MatcherResult {
if self.nans_are_equal && self.expected.is_nan() && actual.is_nan() {
return MatcherResult::Match;
}
- let delta = *actual - self.expected;
+ let delta = actual - self.expected;
if delta >= -self.max_abs_error && delta <= self.max_abs_error {
MatcherResult::Match
} else {
@@ -196,15 +196,14 @@
#[cfg(test)]
mod tests {
- use super::{approx_eq, near};
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
#[test]
fn matches_value_inside_range() -> Result<()> {
let matcher = near(1.0f64, 0.1f64);
- let result = matcher.matches(&1.0f64);
+ let result = matcher.matches(1.0f64);
verify_that!(result, eq(MatcherResult::Match))
}
@@ -213,7 +212,7 @@
fn matches_value_at_low_end_of_range() -> Result<()> {
let matcher = near(1.0f64, 0.1f64);
- let result = matcher.matches(&0.9f64);
+ let result = matcher.matches(0.9f64);
verify_that!(result, eq(MatcherResult::Match))
}
@@ -222,7 +221,7 @@
fn matches_value_at_high_end_of_range() -> Result<()> {
let matcher = near(1.0f64, 0.25f64);
- let result = matcher.matches(&1.25f64);
+ let result = matcher.matches(1.25f64);
verify_that!(result, eq(MatcherResult::Match))
}
@@ -231,7 +230,7 @@
fn does_not_match_value_below_low_end_of_range() -> Result<()> {
let matcher = near(1.0f64, 0.1f64);
- let result = matcher.matches(&0.899999f64);
+ let result = matcher.matches(0.899999f64);
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -240,7 +239,7 @@
fn does_not_match_value_above_high_end_of_range() -> Result<()> {
let matcher = near(1.0f64, 0.1f64);
- let result = matcher.matches(&1.100001f64);
+ let result = matcher.matches(1.100001f64);
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -249,7 +248,7 @@
fn nan_is_not_near_a_number() -> Result<()> {
let matcher = near(0.0f64, f64::MAX);
- let result = matcher.matches(&f64::NAN);
+ let result = matcher.matches(f64::NAN);
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -283,7 +282,7 @@
fn inf_is_not_near_inf() -> Result<()> {
let matcher = near(f64::INFINITY, f64::MAX);
- let result = matcher.matches(&f64::INFINITY);
+ let result = matcher.matches(f64::INFINITY);
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -292,7 +291,7 @@
fn inf_is_not_near_a_number() -> Result<()> {
let matcher = near(f64::INFINITY, f64::MAX);
- let result = matcher.matches(&f64::MIN);
+ let result = matcher.matches(f64::MIN);
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -301,7 +300,7 @@
fn any_two_numbers_are_within_inf_of_each_other() -> Result<()> {
let matcher = near(f64::MIN, f64::INFINITY);
- let result = matcher.matches(&f64::MAX);
+ let result = matcher.matches(f64::MAX);
verify_that!(result, eq(MatcherResult::Match))
}
diff --git a/crates/googletest/src/matchers/none_matcher.rs b/crates/googletest/src/matchers/none_matcher.rs
index af28932..b7a3555 100644
--- a/crates/googletest/src/matchers/none_matcher.rs
+++ b/crates/googletest/src/matchers/none_matcher.rs
@@ -13,9 +13,8 @@
// limitations under the License.
use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use std::fmt::Debug;
-use std::marker::PhantomData;
/// Matches an `Option` containing `None`.
///
@@ -32,19 +31,29 @@
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
-pub fn none<T: Debug>() -> impl Matcher<ActualT = Option<T>> {
- NoneMatcher::<T> { phantom: Default::default() }
+pub fn none() -> NoneMatcher {
+ NoneMatcher
}
-struct NoneMatcher<T> {
- phantom: PhantomData<T>,
+#[derive(MatcherBase)]
+pub struct NoneMatcher;
+
+impl<T: Debug + Copy> Matcher<Option<T>> for NoneMatcher {
+ fn matches(&self, actual: Option<T>) -> MatcherResult {
+ actual.is_none().into()
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ match matcher_result {
+ MatcherResult::Match => "is none".into(),
+ MatcherResult::NoMatch => "is some(_)".into(),
+ }
+ }
}
-impl<T: Debug> Matcher for NoneMatcher<T> {
- type ActualT = Option<T>;
-
- fn matches(&self, actual: &Option<T>) -> MatcherResult {
- (actual.is_none()).into()
+impl<'a, T: Debug> Matcher<&'a Option<T>> for NoneMatcher {
+ fn matches(&self, actual: &'a Option<T>) -> MatcherResult {
+ actual.is_none().into()
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -57,15 +66,14 @@
#[cfg(test)]
mod tests {
- use super::none;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
#[test]
fn none_matches_option_with_none() -> Result<()> {
- let matcher = none::<i32>();
+ let matcher = none();
- let result = matcher.matches(&None);
+ let result = matcher.matches(None::<i32>);
verify_that!(result, eq(MatcherResult::Match))
}
@@ -74,8 +82,47 @@
fn none_does_not_match_option_with_value() -> Result<()> {
let matcher = none();
- let result = matcher.matches(&Some(0));
+ let result = matcher.matches(Some(0));
verify_that!(result, eq(MatcherResult::NoMatch))
}
+
+ #[test]
+ fn none_matches_option_by_ref() -> Result<()> {
+ verify_that!(None::<String>, none())
+ }
+ #[test]
+ fn none_does_not_match_option_with_value_by_ref() -> Result<()> {
+ verify_that!(Some("123".to_string()), not(none()))
+ }
+
+ #[test]
+ fn none_describe_match_option_by_ref() -> Result<()> {
+ verify_that!(
+ Matcher::<&Option<String>>::describe(&none(), MatcherResult::Match),
+ displays_as(eq("is none"))
+ )
+ }
+ #[test]
+ fn none_describe_no_match_option_by_ref() -> Result<()> {
+ verify_that!(
+ Matcher::<&Option<String>>::describe(&none(), MatcherResult::NoMatch),
+ displays_as(eq("is some(_)"))
+ )
+ }
+
+ #[test]
+ fn none_describe_match_option() -> Result<()> {
+ verify_that!(
+ Matcher::<Option<i32>>::describe(&none(), MatcherResult::Match),
+ displays_as(eq("is none"))
+ )
+ }
+ #[test]
+ fn none_describe_no_match_option() -> Result<()> {
+ verify_that!(
+ Matcher::<Option<i32>>::describe(&none(), MatcherResult::NoMatch),
+ displays_as(eq("is some(_)"))
+ )
+ }
}
diff --git a/crates/googletest/src/matchers/not_matcher.rs b/crates/googletest/src/matchers/not_matcher.rs
index f03d4ce..fa5f192 100644
--- a/crates/googletest/src/matchers/not_matcher.rs
+++ b/crates/googletest/src/matchers/not_matcher.rs
@@ -14,9 +14,9 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches the actual value exactly when the inner matcher does _not_ match.
///
@@ -33,28 +33,24 @@
/// # should_pass().unwrap();
/// # should_fail().unwrap_err();
/// ```
-pub fn not<T: Debug, InnerMatcherT: Matcher<ActualT = T>>(
- inner: InnerMatcherT,
-) -> impl Matcher<ActualT = T> {
- NotMatcher::<T, _> { inner, phantom: Default::default() }
+pub fn not<InnerMatcherT>(inner: InnerMatcherT) -> NotMatcher<InnerMatcherT> {
+ NotMatcher { inner }
}
-struct NotMatcher<T, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct NotMatcher<InnerMatcherT> {
inner: InnerMatcherT,
- phantom: PhantomData<T>,
}
-impl<T: Debug, InnerMatcherT: Matcher<ActualT = T>> Matcher for NotMatcher<T, InnerMatcherT> {
- type ActualT = T;
-
- fn matches(&self, actual: &T) -> MatcherResult {
+impl<T: Debug + Copy, InnerMatcherT: Matcher<T>> Matcher<T> for NotMatcher<InnerMatcherT> {
+ fn matches(&self, actual: T) -> MatcherResult {
match self.inner.matches(actual) {
MatcherResult::Match => MatcherResult::NoMatch,
MatcherResult::NoMatch => MatcherResult::Match,
}
}
- fn explain_match(&self, actual: &T) -> Description {
+ fn explain_match(&self, actual: T) -> Description {
self.inner.explain_match(actual)
}
@@ -69,8 +65,7 @@
#[cfg(test)]
mod tests {
- use super::not;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
@@ -78,7 +73,7 @@
fn matches_when_inner_matcher_does_not_match() -> Result<()> {
let matcher = not(eq(1));
- let result = matcher.matches(&0);
+ let result = matcher.matches(0);
verify_that!(result, eq(MatcherResult::Match))
}
@@ -87,14 +82,14 @@
fn does_not_match_when_inner_matcher_matches() -> Result<()> {
let matcher = not(eq(1));
- let result = matcher.matches(&1);
+ let result = matcher.matches(1);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn match_explanation_references_actual_value() -> Result<()> {
- let result = verify_that!([1], not(container_eq([1])));
+ let result = verify_that!(&[1], not(container_eq([1])));
verify_that!(
result,
diff --git a/crates/googletest/src/matchers/ok_matcher.rs b/crates/googletest/src/matchers/ok_matcher.rs
index 8425b93..f399673 100644
--- a/crates/googletest/src/matchers/ok_matcher.rs
+++ b/crates/googletest/src/matchers/ok_matcher.rs
@@ -14,9 +14,9 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches a `Result` containing `Ok` with a value matched by `inner`.
///
@@ -38,28 +38,55 @@
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// ```
-pub fn ok<T: Debug, E: Debug>(
- inner: impl Matcher<ActualT = T>,
-) -> impl Matcher<ActualT = std::result::Result<T, E>> {
- OkMatcher::<T, E, _> { inner, phantom_t: Default::default(), phantom_e: Default::default() }
+pub fn ok<InnerMatcherT>(inner: InnerMatcherT) -> OkMatcher<InnerMatcherT> {
+ OkMatcher { inner }
}
-struct OkMatcher<T, E, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct OkMatcher<InnerMatcherT> {
inner: InnerMatcherT,
- phantom_t: PhantomData<T>,
- phantom_e: PhantomData<E>,
}
-impl<T: Debug, E: Debug, InnerMatcherT: Matcher<ActualT = T>> Matcher
- for OkMatcher<T, E, InnerMatcherT>
+impl<T: Debug + Copy, E: Debug + Copy, InnerMatcherT: Matcher<T>> Matcher<std::result::Result<T, E>>
+ for OkMatcher<InnerMatcherT>
{
- type ActualT = std::result::Result<T, E>;
+ fn matches(&self, actual: std::result::Result<T, E>) -> MatcherResult {
+ actual.map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
+ }
- fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
+ fn explain_match(&self, actual: std::result::Result<T, E>) -> Description {
+ match actual {
+ Ok(o) => {
+ Description::new().text("which is a success").nested(self.inner.explain_match(o))
+ }
+ Err(_) => "which is an error".into(),
+ }
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ match matcher_result {
+ MatcherResult::Match => format!(
+ "is a success containing a value, which {}",
+ self.inner.describe(MatcherResult::Match)
+ )
+ .into(),
+ MatcherResult::NoMatch => format!(
+ "is an error or a success containing a value, which {}",
+ self.inner.describe(MatcherResult::NoMatch)
+ )
+ .into(),
+ }
+ }
+}
+
+impl<'a, T: Debug, E: Debug, InnerMatcherT: Matcher<&'a T>> Matcher<&'a std::result::Result<T, E>>
+ for OkMatcher<InnerMatcherT>
+{
+ fn matches(&self, actual: &'a std::result::Result<T, E>) -> MatcherResult {
actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
}
- fn explain_match(&self, actual: &Self::ActualT) -> Description {
+ fn explain_match(&self, actual: &'a std::result::Result<T, E>) -> Description {
match actual {
Ok(o) => {
Description::new().text("which is a success").nested(self.inner.explain_match(o))
@@ -86,8 +113,7 @@
#[cfg(test)]
mod tests {
- use super::ok;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
@@ -96,7 +122,7 @@
let matcher = ok(eq(1));
let value: std::result::Result<i32, i32> = Ok(1);
- let result = matcher.matches(&value);
+ let result = matcher.matches(value);
verify_that!(result, eq(MatcherResult::Match))
}
@@ -106,7 +132,7 @@
let matcher = ok(eq(1));
let value: std::result::Result<i32, i32> = Ok(0);
- let result = matcher.matches(&value);
+ let result = matcher.matches(value);
verify_that!(result, eq(MatcherResult::NoMatch))
}
@@ -116,12 +142,30 @@
let matcher = ok(eq(1));
let value: std::result::Result<i32, i32> = Err(1);
- let result = matcher.matches(&value);
+ let result = matcher.matches(value);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
+ fn ok_matches_result_with_value_by_ref() -> Result<()> {
+ let result: std::result::Result<String, String> = Ok("123".into());
+ verify_that!(result, ok(eq("123")))
+ }
+
+ #[test]
+ fn ok_does_not_match_result_with_wrong_value_by_ref() -> Result<()> {
+ let result: std::result::Result<String, String> = Ok("321".into());
+ verify_that!(result, not(ok(eq("123"))))
+ }
+
+ #[test]
+ fn ok_does_not_match_result_with_err_by_ref() -> Result<()> {
+ let result: std::result::Result<String, String> = Err("123".into());
+ verify_that!(result, not(ok(eq("123"))))
+ }
+
+ #[test]
fn ok_full_error_message() -> Result<()> {
let result = verify_that!(Ok::<i32, i32>(1), ok(eq(2)));
@@ -140,15 +184,107 @@
}
#[test]
- fn ok_describe_matches() -> Result<()> {
- let matcher = super::OkMatcher::<i32, i32, _> {
- inner: eq(1),
- phantom_t: Default::default(),
- phantom_e: Default::default(),
- };
+ fn ok_describe_match() -> Result<()> {
+ let matcher = ok(eq(1));
verify_that!(
- matcher.describe(MatcherResult::Match),
+ Matcher::<std::result::Result<i32, i32>>::describe(&matcher, MatcherResult::Match),
displays_as(eq("is a success containing a value, which is equal to 1"))
)
}
+
+ #[test]
+ fn ok_describe_no_match() -> Result<()> {
+ let matcher = ok(eq(1));
+ verify_that!(
+ Matcher::<std::result::Result<i32, i32>>::describe(&matcher, MatcherResult::NoMatch),
+ displays_as(eq("is an error or a success containing a value, which isn't equal to 1"))
+ )
+ }
+
+ #[test]
+ fn ok_describe_match_by_ref() -> Result<()> {
+ let matcher = ok(eq(&1));
+ verify_that!(
+ Matcher::<&std::result::Result<i32, String>>::describe(&matcher, MatcherResult::Match),
+ displays_as(eq("is a success containing a value, which is equal to 1"))
+ )
+ }
+
+ #[test]
+ fn ok_describe_no_match_by_ref() -> Result<()> {
+ let matcher = ok(eq(&1));
+ verify_that!(
+ Matcher::<&std::result::Result<i32, String>>::describe(
+ &matcher,
+ MatcherResult::NoMatch
+ ),
+ displays_as(eq("is an error or a success containing a value, which isn't equal to 1"))
+ )
+ }
+
+ #[test]
+ fn ok_explain_match_ok_success() -> Result<()> {
+ let actual = Ok(1);
+ let matcher = ok(eq(1));
+
+ verify_that!(
+ Matcher::<std::result::Result<i32, i32>>::explain_match(&matcher, actual),
+ displays_as(eq("which is a success\n which is equal to 1"))
+ )
+ }
+
+ #[test]
+ fn ok_explain_match_ok_fail() -> Result<()> {
+ let actual = Ok(1);
+ let matcher = ok(eq(2));
+
+ verify_that!(
+ Matcher::<std::result::Result<i32, i32>>::explain_match(&matcher, actual),
+ displays_as(eq("which is a success\n which isn't equal to 2"))
+ )
+ }
+
+ #[test]
+ fn ok_explain_match_ok_err() -> Result<()> {
+ let actual = Err(1);
+ let matcher = ok(eq(2));
+
+ verify_that!(
+ Matcher::<std::result::Result<i32, i32>>::explain_match(&matcher, actual),
+ displays_as(eq("which is an error"))
+ )
+ }
+
+ #[test]
+ fn ok_explain_match_ok_success_by_ref() -> Result<()> {
+ let actual = Ok("123".to_string());
+ let matcher = ok(eq("123"));
+
+ verify_that!(
+ Matcher::<&std::result::Result<String, String>>::explain_match(&matcher, &actual),
+ displays_as(eq("which is a success\n which is equal to \"123\""))
+ )
+ }
+
+ #[test]
+ fn ok_explain_match_ok_fail_by_ref() -> Result<()> {
+ let actual = Ok("321".to_string());
+ let matcher = ok(eq("123"));
+
+ verify_that!(
+ Matcher::<&std::result::Result<String, String>>::explain_match(&matcher, &actual),
+ displays_as(eq("which is a success\n which isn't equal to \"123\""))
+ )
+ }
+
+ #[test]
+ fn ok_explain_match_ok_err_by_ref() -> Result<()> {
+ let actual = Err("123".to_string());
+ let matcher = ok(eq("123"));
+
+ verify_that!(
+ Matcher::<&std::result::Result<String, String>>::explain_match(&matcher, &actual),
+ displays_as(eq("which is an error"))
+ )
+ }
}
diff --git a/crates/googletest/src/matchers/points_to_matcher.rs b/crates/googletest/src/matchers/points_to_matcher.rs
index 2c516d0..0653e03 100644
--- a/crates/googletest/src/matchers/points_to_matcher.rs
+++ b/crates/googletest/src/matchers/points_to_matcher.rs
@@ -13,55 +13,46 @@
// limitations under the License.
use crate::description::Description;
-use crate::matcher::{Matcher, MatcherResult};
+use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use std::fmt::Debug;
-use std::marker::PhantomData;
-use std::ops::Deref;
-/// Matches a (smart) pointer pointing to a value matched by the [`Matcher`]
+/// Matches a reference pointing to a value matched by the [`Matcher`]
/// `expected`.
///
-/// This allows easily matching smart pointers such as `Box`, `Rc`, and `Arc`.
+/// This is useful for combining matchers, especially when working with
+/// iterators.
+///
/// For example:
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
-/// verify_that!(Box::new(123), points_to(eq(123)))?;
+/// verify_that!(&123, points_to(eq(123)))?;
+/// verify_that!(vec![1,2,3], each(points_to(gt(0))))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
-pub fn points_to<ExpectedT, MatcherT, ActualT>(
- expected: MatcherT,
-) -> impl Matcher<ActualT = ActualT>
-where
- ExpectedT: Debug,
- MatcherT: Matcher<ActualT = ExpectedT>,
- ActualT: Deref<Target = ExpectedT> + Debug + ?Sized,
-{
- PointsToMatcher { expected, phantom: Default::default() }
+pub fn points_to<MatcherT>(expected: MatcherT) -> PointsToMatcher<MatcherT> {
+ PointsToMatcher { expected }
}
-struct PointsToMatcher<ActualT: ?Sized, MatcherT> {
+#[derive(MatcherBase)]
+pub struct PointsToMatcher<MatcherT> {
expected: MatcherT,
- phantom: PhantomData<ActualT>,
}
-impl<ExpectedT, MatcherT, ActualT> Matcher for PointsToMatcher<ActualT, MatcherT>
+impl<'a, ExpectedT, MatcherT> Matcher<&'a ExpectedT> for PointsToMatcher<MatcherT>
where
- ExpectedT: Debug,
- MatcherT: Matcher<ActualT = ExpectedT>,
- ActualT: Deref<Target = ExpectedT> + Debug + ?Sized,
+ ExpectedT: Debug + Copy,
+ MatcherT: Matcher<ExpectedT>,
{
- type ActualT = ActualT;
-
- fn matches(&self, actual: &ActualT) -> MatcherResult {
- self.expected.matches(actual.deref())
+ fn matches(&self, actual: &'a ExpectedT) -> MatcherResult {
+ self.expected.matches(*actual)
}
- fn explain_match(&self, actual: &ActualT) -> Description {
- self.expected.explain_match(actual.deref())
+ fn explain_match(&self, actual: &'a ExpectedT) -> Description {
+ self.expected.explain_match(*actual)
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -71,36 +62,24 @@
#[cfg(test)]
mod tests {
- use super::points_to;
use crate::prelude::*;
use indoc::indoc;
- use std::rc::Rc;
#[test]
- fn points_to_matches_box_of_int_with_int() -> Result<()> {
- verify_that!(Box::new(123), points_to(eq(123)))
- }
-
- #[test]
- fn points_to_matches_rc_of_int_with_int() -> Result<()> {
- verify_that!(Rc::new(123), points_to(eq(123)))
- }
-
- #[test]
- fn points_to_matches_box_of_owned_string_with_string_reference() -> Result<()> {
- verify_that!(Rc::new("A string".to_string()), points_to(eq("A string")))
+ fn points_to_matches_ref() -> Result<()> {
+ verify_that!(&123, points_to(eq(123)))
}
#[test]
fn match_explanation_references_actual_value() -> Result<()> {
- let result = verify_that!(&vec![1], points_to(container_eq([])));
+ let result = verify_that!(&1, points_to(eq(0)));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
"
- Actual: [1],
- which contains the unexpected element 1
+ Actual: 1,
+ which isn't equal to 0
"
))))
)
diff --git a/crates/googletest/src/matchers/pointwise_matcher.rs b/crates/googletest/src/matchers/pointwise_matcher.rs
index 01e70c0..a3a0d74 100644
--- a/crates/googletest/src/matchers/pointwise_matcher.rs
+++ b/crates/googletest/src/matchers/pointwise_matcher.rs
@@ -27,13 +27,14 @@
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let value = vec![1, 2, 3];
-/// verify_that!(value, pointwise!(le, [1, 3, 3]))?; // Passes
-/// verify_that!(value, pointwise!(le, vec![1, 3, 3]))?; // Passes
+/// verify_that!(value, pointwise!(le, [&1, &2, &3]))?; // Passes
+/// verify_that!(value, pointwise!(|e| points_to(le(e)), [1, 2, 3]))?; // Passes
+/// verify_that!(value, pointwise!(|e| points_to(le(e)), vec![1, 3, 3]))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// # let value = vec![1, 2, 3];
-/// verify_that!(value, pointwise!(le, [1, 1, 3]))?; // Fails
+/// verify_that!(value, pointwise!(|e| points_to(le(e)), [1, 1, 3]))?; // Fails
/// # Ok(())
/// # }
/// # should_pass().unwrap();
@@ -46,7 +47,7 @@
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let value = vec![1.00001, 2.000001, 3.00001];
-/// verify_that!(value, pointwise!(|v| near(v, 0.001), [1.0, 2.0, 3.0]))?;
+/// verify_that!(value, pointwise!(|v| points_to(near(v, 0.001)), [1.0, 2.0, 3.0]))?;
/// # Ok(())
/// # }
/// # should_pass().unwrap();
@@ -59,12 +60,11 @@
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let value = vec![1.00001, 2.000001, 3.00001];
-/// verify_that!(value, pointwise!(|v, t| near(v, t), [1.0, 2.0, 3.0], [0.001, 0.0001, 0.01]))?;
-/// verify_that!(value, pointwise!(near, [1.0, 2.0, 3.0], [0.001, 0.0001, 0.01]))?; // Same as above
+/// verify_that!(value, pointwise!(|v, t| points_to(near(v, t)), [1.0, 2.0, 3.0], [0.001, 0.0001, 0.01]))?;
/// verify_that!(
/// value,
/// pointwise!(
-/// |v, t, u| near(v, t * u),
+/// |v, t, u| points_to(near(v, t * u)),
/// [1.0, 2.0, 3.0],
/// [0.001, 0.0001, 0.01],
/// [0.5, 0.5, 1.0]
@@ -79,25 +79,20 @@
/// that all of the containers have the same size. This matcher does not check
/// whether the sizes match.
///
-/// The actual value must be a container such as a `Vec`, an array, or a
-/// dereferenced slice. More precisely, a shared borrow of the actual value must
-/// implement [`IntoIterator`].
+/// The actual value must be a container such as a `&Vec`, an array, or a
+/// slice. More precisely, the actual value must implement [`IntoIterator`].
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let value = vec![1, 2, 3];
-/// verify_that!(*value.as_slice(), pointwise!(le, [1, 3, 3]))?; // Passes
+/// verify_that!(value, pointwise!(|i| points_to(le(i)), [1, 3, 3]))?; // Passes
/// verify_that!([1, 2, 3], pointwise!(le, [1, 3, 3]))?; // Passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
-/// This matcher does not support matching directly against an [`Iterator`]. To
-/// match against an iterator, use [`Iterator::collect`] to build a [`Vec`]
-/// first.
-///
/// The second argument can be any value implementing `IntoIterator`, such as a
/// `Vec` or an array. The container does not have to have the same type as the
/// actual value, but the value type must be the same.
@@ -119,13 +114,13 @@
#[doc(hidden)]
macro_rules! __pointwise {
($matcher:expr, $container:expr) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher;
- PointwiseMatcher::new($container.into_iter().map($matcher).collect())
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher::new(
+ $container.into_iter().map($matcher).collect(),
+ )
}};
($matcher:expr, $left_container:expr, $right_container:expr) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher;
- PointwiseMatcher::new(
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher::new(
$left_container
.into_iter()
.zip($right_container.into_iter())
@@ -135,8 +130,7 @@
}};
($matcher:expr, $left_container:expr, $middle_container:expr, $right_container:expr) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher;
- PointwiseMatcher::new(
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::PointwiseMatcher::new(
$left_container
.into_iter()
.zip($right_container.into_iter().zip($middle_container.into_iter()))
@@ -152,33 +146,31 @@
#[doc(hidden)]
pub mod internal {
use crate::description::Description;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use crate::matcher_support::zipped_iterator::zip;
- use std::{fmt::Debug, marker::PhantomData};
+ use std::fmt::Debug;
/// This struct is meant to be used only through the `pointwise` macro.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
- pub struct PointwiseMatcher<ContainerT: ?Sized, MatcherT> {
+ #[derive(MatcherBase)]
+ pub struct PointwiseMatcher<MatcherT> {
matchers: Vec<MatcherT>,
- phantom: PhantomData<ContainerT>,
}
- impl<ContainerT: ?Sized, MatcherT> PointwiseMatcher<ContainerT, MatcherT> {
+ impl<MatcherT> PointwiseMatcher<MatcherT> {
pub fn new(matchers: Vec<MatcherT>) -> Self {
- Self { matchers, phantom: Default::default() }
+ Self { matchers }
}
}
- impl<T: Debug, MatcherT: Matcher<ActualT = T>, ContainerT: ?Sized + Debug> Matcher
- for PointwiseMatcher<ContainerT, MatcherT>
+ impl<T: Debug + Copy, MatcherT: Matcher<T>, ContainerT: Copy + Debug> Matcher<ContainerT>
+ for PointwiseMatcher<MatcherT>
where
- for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
+ ContainerT: IntoIterator<Item = T>,
{
- type ActualT = ContainerT;
-
- fn matches(&self, actual: &ContainerT) -> MatcherResult {
+ fn matches(&self, actual: ContainerT) -> MatcherResult {
let mut zipped_iterator = zip(actual.into_iter(), self.matchers.iter());
for (element, matcher) in zipped_iterator.by_ref() {
if matcher.matches(element).is_no_match() {
@@ -192,7 +184,7 @@
}
}
- fn explain_match(&self, actual: &ContainerT) -> Description {
+ fn explain_match(&self, actual: ContainerT) -> Description {
// TODO(b/260819741) This code duplicates elements_are_matcher.rs. Consider
// extract as a separate library. (or implement pointwise! with
// elements_are)
diff --git a/crates/googletest/src/matchers/predicate_matcher.rs b/crates/googletest/src/matchers/predicate_matcher.rs
index 5bc067d..1ba81c4 100644
--- a/crates/googletest/src/matchers/predicate_matcher.rs
+++ b/crates/googletest/src/matchers/predicate_matcher.rs
@@ -14,43 +14,37 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Creates a matcher based on the predicate provided.
///
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
-/// verify_that!(3, predicate(|x: &i32| x % 2 == 1))?; // Passes
+/// verify_that!(3, predicate(|x: i32| x % 2 == 1))?; // Passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
/// ```
///
-/// The predicate should take the subject type by reference and return a
+/// The predicate should take the subject type and return a
/// boolean.
///
/// Note: even if the Rust compiler should be able to infer the type of
/// the closure argument, it is likely that it won't.
/// See <https://github.com/rust-lang/rust/issues/12679> for update on this issue.
/// This is easily fixed by explicitly declaring the type of the argument
-pub fn predicate<T: Debug + ?Sized, P>(
- predicate: P,
-) -> PredicateMatcher<T, P, NoDescription, NoDescription>
-where
- for<'a> P: Fn(&'a T) -> bool,
-{
+pub fn predicate<P>(predicate: P) -> PredicateMatcher<P, NoDescription, NoDescription> {
PredicateMatcher {
predicate,
positive_description: NoDescription,
negative_description: NoDescription,
- phantom: Default::default(),
}
}
-impl<T, P> PredicateMatcher<T, P, NoDescription, NoDescription> {
+impl<P> PredicateMatcher<P, NoDescription, NoDescription> {
/// Configures this instance to provide a more meaningful description.
///
/// For example, to make sure the error message is more useful
@@ -58,7 +52,7 @@
/// ```
/// # use googletest::matchers::{predicate, PredicateMatcher};
/// # let _ =
- /// predicate(|x: &i32| x % 2 == 1)
+ /// predicate(|x: i32| x % 2 == 1)
/// .with_description("is odd", "is even")
/// # ;
/// ```
@@ -70,24 +64,19 @@
self,
positive_description: D1,
negative_description: D2,
- ) -> PredicateMatcher<T, P, D1, D2> {
- PredicateMatcher {
- predicate: self.predicate,
- positive_description,
- negative_description,
- phantom: Default::default(),
- }
+ ) -> PredicateMatcher<P, D1, D2> {
+ PredicateMatcher { predicate: self.predicate, positive_description, negative_description }
}
}
/// A matcher which applies `predicate` on the value.
///
/// See [`predicate`].
-pub struct PredicateMatcher<T: ?Sized, P, D1, D2> {
+#[derive(MatcherBase)]
+pub struct PredicateMatcher<P, D1, D2> {
predicate: P,
positive_description: D1,
negative_description: D2,
- phantom: PhantomData<T>,
}
/// A trait to allow [`PredicateMatcher::with_description`] to accept multiple
@@ -124,13 +113,11 @@
#[doc(hidden)]
pub struct NoDescription;
-impl<T: Debug, P> Matcher for PredicateMatcher<T, P, NoDescription, NoDescription>
+impl<T: Debug + Copy, P> Matcher<T> for PredicateMatcher<P, NoDescription, NoDescription>
where
- for<'a> P: Fn(&'a T) -> bool,
+ P: Fn(T) -> bool,
{
- type ActualT = T;
-
- fn matches(&self, actual: &T) -> MatcherResult {
+ fn matches(&self, actual: T) -> MatcherResult {
(self.predicate)(actual).into()
}
@@ -142,14 +129,12 @@
}
}
-impl<T: Debug, P, D1: PredicateDescription, D2: PredicateDescription> Matcher
- for PredicateMatcher<T, P, D1, D2>
+impl<T: Debug + Copy, P, D1: PredicateDescription, D2: PredicateDescription> Matcher<T>
+ for PredicateMatcher<P, D1, D2>
where
- for<'a> P: Fn(&'a T) -> bool,
+ P: Fn(T) -> bool,
{
- type ActualT = T;
-
- fn matches(&self, actual: &T) -> MatcherResult {
+ fn matches(&self, actual: T) -> MatcherResult {
(self.predicate)(actual).into()
}
@@ -163,12 +148,10 @@
#[cfg(test)]
mod tests {
- use super::predicate;
- use crate::matcher::Matcher;
use crate::prelude::*;
// Simple matcher with a description
- fn is_odd() -> impl Matcher<ActualT = i32> {
+ fn is_odd() -> impl Matcher<i32> {
predicate(|x| x % 2 == 1).with_description("is odd", "is even")
}
@@ -179,16 +162,16 @@
#[test]
fn predicate_matcher_odd_explain_match_matches() -> Result<()> {
- verify_that!(is_odd().explain_match(&1), displays_as(eq("which is odd")))
+ verify_that!(is_odd().explain_match(1), displays_as(eq("which is odd")))
}
#[test]
fn predicate_matcher_odd_explain_match_does_not_match() -> Result<()> {
- verify_that!(is_odd().explain_match(&2), displays_as(eq("which is even")))
+ verify_that!(is_odd().explain_match(2), displays_as(eq("which is even")))
}
// Simple Matcher without description
- fn is_even() -> impl Matcher<ActualT = i32> {
+ fn is_even() -> impl Matcher<i32> {
predicate(|x| x % 2 == 0)
}
@@ -199,18 +182,18 @@
#[test]
fn predicate_matcher_even_explain_match_matches() -> Result<()> {
- verify_that!(is_even().explain_match(&2), displays_as(eq("which matches")))
+ verify_that!(is_even().explain_match(2), displays_as(eq("which matches")))
}
#[test]
fn predicate_matcher_even_explain_match_does_not_match() -> Result<()> {
- verify_that!(is_even().explain_match(&1), displays_as(eq("which does not match")))
+ verify_that!(is_even().explain_match(1), displays_as(eq("which does not match")))
}
#[test]
fn predicate_matcher_generator_lambda() -> Result<()> {
let is_divisible_by = |quotient| {
- predicate(move |x: &i32| x % quotient == 0).with_description(
+ predicate(move |x: i32| x % quotient == 0).with_description(
move || format!("is divisible by {quotient}"),
move || format!("is not divisible by {quotient}"),
)
@@ -220,12 +203,12 @@
#[test]
fn predicate_matcher_inline() -> Result<()> {
- verify_that!(2048, predicate(|x: &i32| x.count_ones() == 1))
+ verify_that!(2048, predicate(|x: i32| x.count_ones() == 1))
}
#[test]
fn predicate_matcher_function_pointer() -> Result<()> {
use std::time::Duration;
- verify_that!(Duration::new(0, 0), predicate(Duration::is_zero))
+ verify_that!(&Duration::new(0, 0), predicate(Duration::is_zero))
}
}
diff --git a/crates/googletest/src/matchers/property_matcher.rs b/crates/googletest/src/matchers/property_matcher.rs
index 19b4862..98c51a6 100644
--- a/crates/googletest/src/matchers/property_matcher.rs
+++ b/crates/googletest/src/matchers/property_matcher.rs
@@ -35,7 +35,26 @@
/// }
///
/// let value = vec![MyStruct { a_field: 100 }];
-/// verify_that!(value, contains(property!(MyStruct.get_a_field(), eq(100))))
+/// verify_that!(value, contains(property!(&MyStruct.get_a_field(), eq(100))))
+/// # .unwrap();
+/// ```
+///
+///
+/// If the inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// pub struct MyStruct {
+/// a_field: u32,
+/// }
+///
+/// impl MyStruct {
+/// pub fn get_a_field(&self) -> u32 { self.a_field }
+/// }
+///
+/// let value = vec![MyStruct { a_field: 100 }];
+/// verify_that!(value, contains(property!(&MyStruct.get_a_field(), 100)))
/// # .unwrap();
/// ```
///
@@ -44,24 +63,7 @@
/// failure, it will be invoked a second time, with the assertion failure output
/// reflecting the *second* invocation.
///
-/// If the method returns a *reference*, then it must be preceded by a `*`:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # #[derive(Debug)]
-/// # pub struct MyStruct {
-/// # a_field: u32,
-/// # }
-/// impl MyStruct {
-/// pub fn get_a_field(&self) -> &u32 { &self.a_field }
-/// }
-///
-/// # let value = vec![MyStruct { a_field: 100 }];
-/// verify_that!(value, contains(property!(*MyStruct.get_a_field(), eq(100))))
-/// # .unwrap();
-/// ```
-///
-/// The method may also take additional arguments:
+/// The method may also take additional litteral arguments:
///
/// ```
/// # use googletest::prelude::*;
@@ -74,26 +76,88 @@
/// }
///
/// # let value = vec![MyStruct { a_field: 100 }];
-/// verify_that!(value, contains(property!(MyStruct.add_to_a_field(50), eq(150))))
+/// verify_that!(value, contains(property!(&MyStruct.add_to_a_field(50), eq(150))))
/// # .unwrap();
/// ```
///
-/// Unfortunately, this matcher does *not* work with methods returning string
-/// slices:
+/// The arguments must be litteral as `property!` is not able to capture them.
///
-/// ```compile_fail
+/// # Specification of the property pattern
+///
+/// The specification of the field follow the syntax: `(ref)? (&)?
+/// $TYPE.$PROPERTY\($ARGUMENT\)`.
+///
+/// The `&` allows to specify whether this matcher matches against an actual of
+/// type `$TYPE` (`$TYPE` must implement `Copy`) or a `&$TYPE`.
+///
+/// For instance:
+///
+/// ```
/// # use googletest::prelude::*;
/// #[derive(Debug)]
-/// pub struct MyStruct {
-/// a_string: String,
-/// }
-/// impl MyStruct {
-/// pub fn get_a_string(&self) -> &str { &self.a_string }
-/// }
+/// pub struct AStruct;
///
-/// let value = MyStruct { a_string: "A string".into() };
-/// verify_that!(value, property!(*MyStruct.get_a_string(), eq("A string"))) // Does not compile
-/// # .unwrap();
+/// impl AStruct {
+/// fn a_property(&self) -> i32 {32}
+/// }
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct, property!(&AStruct.a_property(), eq(32)))?;
+/// # Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug, Clone, Copy)]
+/// pub struct AStruct;
+///
+/// impl AStruct {
+/// fn a_property(self) -> i32 {32}
+/// }
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct, property!(AStruct.a_property(), eq(32)))?;
+/// # Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// The `ref` allows to bind the property returned value by reference, which is
+/// required if the field type does not implement `Copy`.
+///
+/// For instance:
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// pub struct AStruct;
+///
+/// impl AStruct {
+/// fn a_property(&self) -> i32 {32}
+/// }
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct, property!(&AStruct.a_property(), eq(32)))?;
+/// # Ok(())
+/// # }
+/// # should_pass().unwrap();
+/// ```
+///
+/// If `property!` is qualified by both `&` and `ref`, they can both be omitted.
+///
+/// ```
+/// # use googletest::prelude::*;
+/// #[derive(Debug)]
+/// pub struct AStruct;
+///
+/// impl AStruct {
+/// fn a_property(&self) -> String {"32".into()}
+/// }
+/// # fn should_pass() -> Result<()> {
+/// verify_that!(AStruct, property!(&AStruct.a_property(), ref eq("32")))?;
+/// verify_that!(AStruct, property!(AStruct.a_property(), eq("32")))?;
+/// # Ok(())
+/// # }
+/// # should_pass().unwrap();
/// ```
///
/// This macro is analogous to [`field`][crate::matchers::field], except that it
@@ -112,20 +176,30 @@
#[doc(hidden)]
#[macro_export]
macro_rules! property_internal {
- ($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher;
- property_matcher(
- |o: &$($t)::+| o.$method($($argument),*),
- &stringify!($method($($argument),*)),
- $m)
- }};
- (* $($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher;
- property_ref_matcher(
+ (&$($t:ident)::+.$method:tt($($argument:tt),* $(,)?), ref $m:expr) => {{
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher(
+ |o: &$($t)::+| $($t)::+::$method(o, $($argument),*),
+ &stringify!($method($($argument),*)),
+ $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
+ }};
+ ($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), ref $m:expr) => {{
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::property_ref_matcher(
+ |o: $($t)::+| $($t)::+::$method(o, $($argument),*),
+ &stringify!($method($($argument),*)),
+ $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
+ }};
+ (& $($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher(
+ |o: &&$($t)::+| o.$method($($argument),*),
+ &stringify!($method($($argument),*)),
+ $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
+ }};
+ ($($t:ident)::+.$method:tt($($argument:tt),* $(,)?), $m:expr) => {{
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::property_matcher(
|o: &$($t)::+| o.$method($($argument),*),
&stringify!($method($($argument),*)),
- $m)
+ $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!($m))
}};
}
@@ -136,37 +210,65 @@
pub mod internal {
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
- use std::{fmt::Debug, marker::PhantomData};
+ use std::fmt::Debug;
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
- pub fn property_matcher<OuterT: Debug, InnerT: Debug, MatcherT: Matcher<ActualT = InnerT>>(
- extractor: impl Fn(&OuterT) -> InnerT,
+ pub fn property_matcher<OuterT: Debug, InnerT: Debug, MatcherT>(
+ extractor: fn(&OuterT) -> InnerT,
property_desc: &'static str,
inner: MatcherT,
- ) -> impl Matcher<ActualT = OuterT> {
- PropertyMatcher { extractor, property_desc, inner, phantom: Default::default() }
+ ) -> PropertyMatcher<OuterT, InnerT, MatcherT> {
+ PropertyMatcher { extractor, property_desc, inner }
}
- struct PropertyMatcher<OuterT, ExtractorT, MatcherT> {
- extractor: ExtractorT,
+ #[derive(MatcherBase)]
+ pub struct PropertyMatcher<OuterT, InnerT, MatcherT> {
+ extractor: fn(&OuterT) -> InnerT,
property_desc: &'static str,
inner: MatcherT,
- phantom: PhantomData<OuterT>,
}
- impl<InnerT, OuterT, ExtractorT, MatcherT> Matcher for PropertyMatcher<OuterT, ExtractorT, MatcherT>
+ impl<InnerT, OuterT, MatcherT> Matcher<OuterT> for PropertyMatcher<OuterT, InnerT, MatcherT>
+ where
+ InnerT: Debug + Copy,
+ OuterT: Debug + Copy,
+ MatcherT: Matcher<InnerT>,
+ {
+ fn matches(&self, actual: OuterT) -> MatcherResult {
+ self.inner.matches((self.extractor)(&actual))
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ format!(
+ "has property `{}`, which {}",
+ self.property_desc,
+ self.inner.describe(matcher_result)
+ )
+ .into()
+ }
+
+ fn explain_match(&self, actual: OuterT) -> Description {
+ let actual_inner = (self.extractor)(&actual);
+ format!(
+ "whose property `{}` is `{:#?}`, {}",
+ self.property_desc,
+ actual_inner,
+ self.inner.explain_match(actual_inner)
+ )
+ .into()
+ }
+ }
+
+ impl<'a, InnerT, OuterT, MatcherT> Matcher<&'a OuterT> for PropertyMatcher<OuterT, InnerT, MatcherT>
where
InnerT: Debug,
OuterT: Debug,
- ExtractorT: Fn(&OuterT) -> InnerT,
- MatcherT: Matcher<ActualT = InnerT>,
+ MatcherT: for<'b> Matcher<&'b InnerT>,
{
- type ActualT = OuterT;
-
- fn matches(&self, actual: &OuterT) -> MatcherResult {
+ fn matches(&self, actual: &'a OuterT) -> MatcherResult {
self.inner.matches(&(self.extractor)(actual))
}
@@ -179,7 +281,7 @@
.into()
}
- fn explain_match(&self, actual: &OuterT) -> Description {
+ fn explain_match(&self, actual: &'a OuterT) -> Description {
let actual_inner = (self.extractor)(actual);
format!(
"whose property `{}` is `{:#?}`, {}",
@@ -193,32 +295,36 @@
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
- pub fn property_ref_matcher<OuterT, InnerT, MatcherT>(
- extractor: fn(&OuterT) -> &InnerT,
+ pub fn property_ref_matcher<OuterT, InnerT, ExtractorT, MatcherT>(
+ extractor: ExtractorT,
property_desc: &'static str,
inner: MatcherT,
- ) -> impl Matcher<ActualT = OuterT>
+ ) -> PropertyRefMatcher<ExtractorT, MatcherT>
where
OuterT: Debug,
- InnerT: Debug + ?Sized,
- MatcherT: Matcher<ActualT = InnerT>,
+ InnerT: Debug,
+ MatcherT: for<'a> Matcher<&'a InnerT>,
+ ExtractorT: Fn(OuterT) -> InnerT,
{
PropertyRefMatcher { extractor, property_desc, inner }
}
- struct PropertyRefMatcher<InnerT: ?Sized, OuterT, MatcherT> {
- extractor: fn(&OuterT) -> &InnerT,
+ #[derive(MatcherBase)]
+ pub struct PropertyRefMatcher<ExtractorT, MatcherT> {
+ extractor: ExtractorT,
property_desc: &'static str,
inner: MatcherT,
}
- impl<InnerT: Debug + ?Sized, OuterT: Debug, MatcherT: Matcher<ActualT = InnerT>> Matcher
- for PropertyRefMatcher<InnerT, OuterT, MatcherT>
+ impl<
+ InnerT: Debug,
+ OuterT: Debug + Copy,
+ MatcherT: for<'a> Matcher<&'a InnerT>,
+ ExtractorT: Fn(OuterT) -> InnerT,
+ > Matcher<OuterT> for PropertyRefMatcher<ExtractorT, MatcherT>
{
- type ActualT = OuterT;
-
- fn matches(&self, actual: &OuterT) -> MatcherResult {
- self.inner.matches((self.extractor)(actual))
+ fn matches(&self, actual: OuterT) -> MatcherResult {
+ self.inner.matches(&(self.extractor)(actual))
}
fn describe(&self, matcher_result: MatcherResult) -> Description {
@@ -230,13 +336,13 @@
.into()
}
- fn explain_match(&self, actual: &OuterT) -> Description {
+ fn explain_match(&self, actual: OuterT) -> Description {
let actual_inner = (self.extractor)(actual);
format!(
"whose property `{}` is `{:#?}`, {}",
self.property_desc,
actual_inner,
- self.inner.explain_match(actual_inner)
+ self.inner.explain_match(&actual_inner)
)
.into()
}
diff --git a/crates/googletest/src/matchers/result_of_matcher.rs b/crates/googletest/src/matchers/result_of_matcher.rs
new file mode 100644
index 0000000..c75fe66
--- /dev/null
+++ b/crates/googletest/src/matchers/result_of_matcher.rs
@@ -0,0 +1,440 @@
+// Copyright 2024 Google LLC
+//
+// 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.
+
+/// Matches a value where the result of `callable` applied to the value matches
+/// the inner matcher.
+///
+/// The `callable` will be called twice, so make sure it is pure.
+/// ```
+/// use googletest::prelude::*;
+/// fn should_pass() -> googletest::Result<()> {
+/// verify_that!(100, result_of!(|value| value + 1, eq(101)))?; // Passes
+/// Ok(())
+/// }
+///
+/// fn should_fail() -> googletest::Result<()> {
+/// verify_that!(100, result_of!(|value| value * 2, eq(100)))?; // Fails
+/// Ok(())
+/// }
+/// should_pass().unwrap();
+/// should_fail().unwrap_err();
+/// ```
+#[macro_export]
+macro_rules! __result_of {
+ ($function: expr, $matcher: expr) => {{
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::result_of(
+ $function,
+ $matcher,
+ stringify!($function),
+ )
+ }};
+}
+
+/// Matches a value where the reference to the result of `callable` applied to
+/// the value matches the inner matcher.
+///
+/// The `callable` will be called twice, so make sure it is pure.
+/// ```
+/// use googletest::prelude::*;
+/// fn should_pass_1() -> googletest::Result<()> {
+/// verify_that!("hello", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))?; // Passes
+/// Ok(())
+/// }
+///
+/// fn should_pass_2() -> googletest::Result<()> {
+/// verify_that!(100, result_of_ref!(|value| value + 1, eq(&101)))?; // Passes
+/// Ok(())
+/// }
+///
+/// fn should_fail() -> googletest::Result<()> {
+/// verify_that!("world", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))?; // Passes
+/// Ok(())
+/// }
+/// should_pass_1().unwrap();
+/// should_pass_2().unwrap();
+/// should_fail().unwrap_err();
+/// ```
+#[macro_export]
+macro_rules! __result_of_ref {
+ ($function: expr, $matcher: expr) => {{
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::result_of_ref(
+ $function,
+ $matcher,
+ stringify!($function),
+ )
+ }};
+}
+
+/// Items for use only by the declarative macros in this module.
+///
+/// **For internal use only. API stability is not guaranteed!**
+#[doc(hidden)]
+pub mod internal {
+ use crate::description::Description;
+ use crate::matcher::{Matcher, MatcherBase, MatcherResult};
+ use std::fmt::Debug;
+
+ pub fn result_of<Callable, InnerMatcher>(
+ callable: Callable,
+ inner_matcher: InnerMatcher,
+ callable_description: &'static str,
+ ) -> ResultOfMatcher<Callable, InnerMatcher> {
+ ResultOfMatcher { callable, inner_matcher, callable_description }
+ }
+
+ #[derive(MatcherBase)]
+ pub struct ResultOfMatcher<Callable, InnerMatcher> {
+ callable: Callable,
+ inner_matcher: InnerMatcher,
+ callable_description: &'static str,
+ }
+
+ impl<I: Copy + Debug, T: Debug + Copy, CallableT: Fn(I) -> T, InnerMatcherT: Matcher<T>>
+ Matcher<I> for ResultOfMatcher<CallableT, InnerMatcherT>
+ {
+ fn matches(&self, actual: I) -> MatcherResult {
+ self.inner_matcher.matches((self.callable)(actual))
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ Description::new()
+ .text(format!("by applying {},", self.callable_description))
+ .nested(self.inner_matcher.describe(matcher_result))
+ }
+
+ fn explain_match(&self, actual: I) -> Description {
+ let actual_result = (self.callable)(actual);
+ Description::new()
+ .text(format!("which, results into {actual_result:?}",))
+ .nested(self.describe(self.matches(actual)))
+ }
+ }
+
+ pub fn result_of_ref<Callable, InnerMatcher>(
+ callable: Callable,
+ inner_matcher: InnerMatcher,
+ callable_description: &'static str,
+ ) -> ResultOfRefMatcher<Callable, InnerMatcher> {
+ ResultOfRefMatcher { callable, inner_matcher, callable_description }
+ }
+ #[derive(MatcherBase)]
+ pub struct ResultOfRefMatcher<Callable, InnerMatcher> {
+ callable: Callable,
+ inner_matcher: InnerMatcher,
+ callable_description: &'static str,
+ }
+
+ impl<
+ I: Copy + Debug,
+ T: Debug,
+ Callable: Fn(I) -> T,
+ InnerMatcherT: for<'a> Matcher<&'a T>,
+ > Matcher<I> for ResultOfRefMatcher<Callable, InnerMatcherT>
+ {
+ fn matches(&self, actual: I) -> MatcherResult {
+ self.inner_matcher.matches(&(self.callable)(actual))
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ Description::new()
+ .text(format!("by applying {},", self.callable_description))
+ .nested(self.inner_matcher.describe(matcher_result))
+ }
+
+ fn explain_match(&self, actual: I) -> Description {
+ let actual_result = (self.callable)(actual);
+ Description::new()
+ .text(format!("which, results into {actual_result:?}",))
+ .nested(self.describe(self.matches(actual)))
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::prelude::*;
+ use indoc::indoc;
+
+ #[test]
+ fn result_of_match_with_value() -> Result<()> {
+ verify_that!(1, result_of!(|value| value + 1, eq(2)))
+ }
+
+ #[test]
+ fn result_of_match_with_value_function() -> Result<()> {
+ fn inc_by_one(value: i32) -> i32 {
+ value + 1
+ }
+ verify_that!(1, result_of!(inc_by_one, eq(2)))
+ }
+
+ #[test]
+ fn result_of_match_with_different_value() -> Result<()> {
+ let result = verify_that!(0, result_of!(|value| value - 1, eq(2)));
+ verify_that!(
+ result,
+ err(displays_as(contains_substring(indoc!(
+ "
+ Value of: 0
+ Expected: by applying |value| value - 1,
+ is equal to 2
+ Actual: 0,
+ which, results into -1
+ by applying |value| value - 1,
+ isn't equal to 2
+ "
+ ))))
+ )
+ }
+
+ #[test]
+ fn result_of_match_with_different_value_block_closure() -> Result<()> {
+ let result = verify_that!(0, result_of!(|value| { value - 1 }, eq(2)));
+ verify_that!(
+ result,
+ err(displays_as(contains_substring(indoc!(
+ "
+ Value of: 0
+ Expected: by applying |value| { value - 1 },
+ is equal to 2
+ Actual: 0,
+ which, results into -1
+ by applying |value| { value - 1 },
+ isn't equal to 2
+ "
+ ))))
+ )
+ }
+
+ #[test]
+ fn result_of_match_with_different_value_multiline_closure() -> Result<()> {
+ let result = verify_that!(
+ 0,
+ result_of!(
+ |value| {
+ let dec = value - 1;
+ let inc = dec + 1;
+ inc - 2
+ },
+ eq(2)
+ )
+ );
+ verify_that!(
+ result,
+ err(displays_as(contains_substring(indoc!(
+ "
+ Value of: 0
+ Expected: by applying |value| { let dec = value - 1; let inc = dec + 1; inc - 2 },
+ is equal to 2
+ Actual: 0,
+ which, results into -2
+ by applying |value| { let dec = value - 1; let inc = dec + 1; inc - 2 },
+ isn't equal to 2
+ "
+ ))))
+ )
+ }
+
+ #[test]
+ fn result_of_match_with_different_value_function() -> Result<()> {
+ fn dec_by_one(value: i32) -> i32 {
+ value - 1
+ }
+ let result = verify_that!(0, result_of!(dec_by_one, eq(2)));
+ verify_that!(
+ result,
+ err(displays_as(contains_substring(indoc!(
+ "
+ Value of: 0
+ Expected: by applying dec_by_one,
+ is equal to 2
+ Actual: 0,
+ which, results into -1
+ by applying dec_by_one,
+ isn't equal to 2
+ "
+ ))))
+ )
+ }
+
+ #[test]
+ fn result_of_ref_match_with_string_reference() -> Result<()> {
+ verify_that!("hello", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")))
+ }
+
+ #[test]
+ fn result_of_ref_match_with_string_reference_function() -> Result<()> {
+ fn to_upper_case<S: AsRef<str>>(s: S) -> String {
+ s.as_ref().to_uppercase()
+ }
+ verify_that!("hello", result_of_ref!(to_upper_case, eq("HELLO")))
+ }
+
+ #[test]
+ fn result_of_ref_match_with_copy_types() -> Result<()> {
+ verify_that!(100, result_of_ref!(|value| value + 1, eq(&101)))
+ }
+
+ #[test]
+ fn result_of_ref_match_with_different_value() -> Result<()> {
+ let result = verify_that!("world", result_of_ref!(|s: &str| s.to_uppercase(), eq("HELLO")));
+ verify_that!(
+ result,
+ err(displays_as(contains_substring(indoc!(
+ r#"
+ Value of: "world"
+ Expected: by applying |s: &str| s.to_uppercase(),
+ is equal to "HELLO"
+ Actual: "world",
+ which, results into "WORLD"
+ by applying |s: &str| s.to_uppercase(),
+ isn't equal to "HELLO""#
+ ))))
+ )
+ }
+
+ #[test]
+ fn result_of_ref_match_with_different_value_block_closure() -> Result<()> {
+ let result =
+ verify_that!("world", result_of_ref!(|s: &str| { s.to_uppercase() }, eq("HELLO")));
+ verify_that!(
+ result,
+ err(displays_as(contains_substring(indoc!(
+ r#"
+ Value of: "world"
+ Expected: by applying |s: &str| { s.to_uppercase() },
+ is equal to "HELLO"
+ Actual: "world",
+ which, results into "WORLD"
+ by applying |s: &str| { s.to_uppercase() },
+ isn't equal to "HELLO"
+ "#
+ ))))
+ )
+ }
+
+ #[test]
+ fn result_of_ref_match_with_different_value_function() -> Result<()> {
+ fn to_upper_case<S: AsRef<str>>(s: S) -> String {
+ s.as_ref().to_uppercase()
+ }
+ let result = verify_that!("world", result_of_ref!(to_upper_case, eq("HELLO")));
+ verify_that!(
+ result,
+ err(displays_as(contains_substring(indoc!(
+ r#"
+ Value of: "world"
+ Expected: by applying to_upper_case,
+ is equal to "HELLO"
+ Actual: "world",
+ which, results into "WORLD"
+ by applying to_upper_case,
+ isn't equal to "HELLO"
+ "#
+ ))))
+ )
+ }
+
+ #[test]
+ fn result_of_ref_match_different_with_closure_variable() -> Result<()> {
+ let to_upper_case = |s: &str| s.to_uppercase();
+ let result = verify_that!("world", result_of_ref!(to_upper_case, eq("HELLO")));
+ verify_that!(
+ result,
+ err(displays_as(contains_substring(indoc!(
+ r#"
+ Value of: "world"
+ Expected: by applying to_upper_case,
+ is equal to "HELLO"
+ Actual: "world",
+ which, results into "WORLD"
+ by applying to_upper_case,
+ isn't equal to "HELLO"
+ "#
+ ))))
+ )
+ }
+
+ #[test]
+ fn result_of_ref_match_different_with_method_literal() -> Result<()> {
+ let result = verify_that!("world", result_of_ref!(str::to_uppercase, eq("HELLO")));
+ verify_that!(
+ result,
+ err(displays_as(contains_substring(indoc!(
+ r#"
+ Value of: "world"
+ Expected: by applying str::to_uppercase,
+ is equal to "HELLO"
+ Actual: "world",
+ which, results into "WORLD"
+ by applying str::to_uppercase,
+ isn't equal to "HELLO"
+ "#
+ ))))
+ )
+ }
+
+ #[test]
+ fn result_of_ref_match_different_with_function_return_closure() -> Result<()> {
+ fn upper_case() -> impl Fn(&str) -> String {
+ |s: &str| s.to_uppercase()
+ }
+ let result = verify_that!("world", result_of_ref!(upper_case(), eq("HELLO")));
+ verify_that!(
+ result,
+ err(displays_as(contains_substring(indoc!(
+ r#"
+ Value of: "world"
+ Expected: by applying upper_case(),
+ is equal to "HELLO"
+ Actual: "world",
+ which, results into "WORLD"
+ by applying upper_case(),
+ isn't equal to "HELLO"
+ "#
+ ))))
+ )
+ }
+
+ #[test]
+ fn test_describe_simple() -> Result<()> {
+ let matcher = result_of!(|x| x + 1, eq(2));
+ let description = matcher.describe(matcher.matches(0));
+ verify_that!(
+ description,
+ displays_as(eq(indoc!(
+ r#"
+ by applying |x| x + 1,
+ isn't equal to 2"#
+ )))
+ )
+ }
+
+ #[test]
+ fn test_describe_complicated() -> Result<()> {
+ let matcher = result_of_ref!(
+ |s: &str| s.chars().collect::<Vec<_>>(),
+ each(predicate(char::is_ascii_alphabetic))
+ );
+ let description = matcher.describe(matcher.matches("A quick brown fox"));
+ verify_that!(
+ description,
+ displays_as(eq(indoc!(
+ r#"
+ by applying |s: &str| s.chars().collect::<Vec<_>>(),
+ contains no element that matches"#
+ )))
+ )
+ }
+}
diff --git a/crates/googletest/src/matchers/some_matcher.rs b/crates/googletest/src/matchers/some_matcher.rs
index 905aa17..8c926f9 100644
--- a/crates/googletest/src/matchers/some_matcher.rs
+++ b/crates/googletest/src/matchers/some_matcher.rs
@@ -14,9 +14,9 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches an `Option` containing a value matched by `inner`.
///
@@ -38,23 +38,51 @@
/// # should_fail_1().unwrap_err();
/// # should_fail_2().unwrap_err();
/// ```
-pub fn some<T: Debug>(inner: impl Matcher<ActualT = T>) -> impl Matcher<ActualT = Option<T>> {
- SomeMatcher { inner, phantom: Default::default() }
+pub fn some<Inner>(inner: Inner) -> SomeMatcher<Inner> {
+ SomeMatcher { inner }
}
-struct SomeMatcher<T, InnerMatcherT> {
+#[derive(MatcherBase)]
+pub struct SomeMatcher<InnerMatcherT> {
inner: InnerMatcherT,
- phantom: PhantomData<T>,
}
-impl<T: Debug, InnerMatcherT: Matcher<ActualT = T>> Matcher for SomeMatcher<T, InnerMatcherT> {
- type ActualT = Option<T>;
+impl<T: Debug + Copy, InnerMatcherT: Matcher<T>> Matcher<Option<T>> for SomeMatcher<InnerMatcherT> {
+ fn matches(&self, actual: Option<T>) -> MatcherResult {
+ actual.map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
+ }
- fn matches(&self, actual: &Option<T>) -> MatcherResult {
+ fn explain_match(&self, actual: Option<T>) -> Description {
+ match (self.matches(actual), actual) {
+ (_, Some(t)) => {
+ Description::new().text("which has a value").nested(self.inner.explain_match(t))
+ }
+ (_, None) => "which is None".into(),
+ }
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ match matcher_result {
+ MatcherResult::Match => {
+ format!("has a value which {}", self.inner.describe(MatcherResult::Match)).into()
+ }
+ MatcherResult::NoMatch => format!(
+ "is None or has a value which {}",
+ self.inner.describe(MatcherResult::NoMatch)
+ )
+ .into(),
+ }
+ }
+}
+
+impl<'a, T: Debug, InnerMatcherT: Matcher<&'a T>> Matcher<&'a Option<T>>
+ for SomeMatcher<InnerMatcherT>
+{
+ fn matches(&self, actual: &'a Option<T>) -> MatcherResult {
actual.as_ref().map(|v| self.inner.matches(v)).unwrap_or(MatcherResult::NoMatch)
}
- fn explain_match(&self, actual: &Option<T>) -> Description {
+ fn explain_match(&self, actual: &'a Option<T>) -> Description {
match (self.matches(actual), actual) {
(_, Some(t)) => {
Description::new().text("which has a value").nested(self.inner.explain_match(t))
@@ -79,8 +107,7 @@
#[cfg(test)]
mod tests {
- use super::some;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
@@ -88,7 +115,7 @@
fn some_matches_option_with_value() -> Result<()> {
let matcher = some(eq(1));
- let result = matcher.matches(&Some(1));
+ let result = matcher.matches(Some(1));
verify_that!(result, eq(MatcherResult::Match))
}
@@ -97,21 +124,36 @@
fn some_does_not_match_option_with_wrong_value() -> Result<()> {
let matcher = some(eq(1));
- let result = matcher.matches(&Some(0));
+ let result = matcher.matches(Some(0));
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
fn some_does_not_match_option_with_none() -> Result<()> {
- let matcher = some(eq::<i32, _>(1));
+ let matcher = some(eq(1));
- let result = matcher.matches(&None);
+ let result = matcher.matches(None::<i32>);
verify_that!(result, eq(MatcherResult::NoMatch))
}
#[test]
+ fn some_matches_option_with_by_ref_value() -> Result<()> {
+ verify_that!(Some("123".to_string()), some(eq("123")))
+ }
+
+ #[test]
+ fn some_does_not_match_option_with_wrong_by_ref_value() -> Result<()> {
+ verify_that!(Some("321".to_string()), not(some(eq("123"))))
+ }
+
+ #[test]
+ fn some_does_not_match_option_with_by_ref_none() -> Result<()> {
+ verify_that!(None::<String>, not(some(eq("123"))))
+ }
+
+ #[test]
fn some_full_error_message() -> Result<()> {
let result = verify_that!(Some(2), some(eq(1)));
verify_that!(
@@ -131,7 +173,7 @@
#[test]
fn some_describe_matches() -> Result<()> {
verify_that!(
- some(eq::<i32, _>(1)).describe(MatcherResult::Match),
+ Matcher::<Option<i32>>::describe(&some(eq(1)), MatcherResult::Match),
displays_as(eq("has a value which is equal to 1"))
)
}
@@ -139,20 +181,36 @@
#[test]
fn some_describe_does_not_match() -> Result<()> {
verify_that!(
- some(eq::<i32, _>(1)).describe(MatcherResult::NoMatch),
+ Matcher::<Option<i32>>::describe(&some(eq(1)), MatcherResult::NoMatch),
displays_as(eq("is None or has a value which isn't equal to 1"))
)
}
#[test]
+ fn some_describe_matches_of_by_ref() -> Result<()> {
+ verify_that!(
+ Matcher::<Option<&String>>::describe(&some(eq("123")), MatcherResult::Match),
+ displays_as(eq("has a value which is equal to \"123\""))
+ )
+ }
+
+ #[test]
+ fn some_describe_does_not_match_of_by_ref() -> Result<()> {
+ verify_that!(
+ Matcher::<Option<&String>>::describe(&some(eq("123")), MatcherResult::NoMatch),
+ displays_as(eq("is None or has a value which isn't equal to \"123\""))
+ )
+ }
+
+ #[test]
fn some_explain_match_with_none() -> Result<()> {
- verify_that!(some(eq::<i32, _>(1)).explain_match(&None), displays_as(eq("which is None")))
+ verify_that!(some(eq(1)).explain_match(None::<i32>), displays_as(eq("which is None")))
}
#[test]
fn some_explain_match_with_some_success() -> Result<()> {
verify_that!(
- some(eq(1)).explain_match(&Some(1)),
+ some(eq(1)).explain_match(Some(1)),
displays_as(eq("which has a value\n which is equal to 1"))
)
}
@@ -160,8 +218,32 @@
#[test]
fn some_explain_match_with_some_fail() -> Result<()> {
verify_that!(
- some(eq(1)).explain_match(&Some(2)),
+ some(eq(1)).explain_match(Some(2)),
displays_as(eq("which has a value\n which isn't equal to 1"))
)
}
+
+ #[test]
+ fn some_explain_match_with_none_by_ref() -> Result<()> {
+ verify_that!(
+ some(eq("123")).explain_match(&None::<String>),
+ displays_as(eq("which is None"))
+ )
+ }
+
+ #[test]
+ fn some_explain_match_with_some_success_by_ref() -> Result<()> {
+ verify_that!(
+ some(eq("123")).explain_match(&Some("123".to_string())),
+ displays_as(eq("which has a value\n which is equal to \"123\""))
+ )
+ }
+
+ #[test]
+ fn some_explain_match_with_some_fail_by_ref() -> Result<()> {
+ verify_that!(
+ some(eq("123")).explain_match(&Some("321".to_string())),
+ displays_as(eq("which has a value\n which isn't equal to \"123\""))
+ )
+ }
}
diff --git a/crates/googletest/src/matchers/str_matcher.rs b/crates/googletest/src/matchers/str_matcher.rs
index b624d44..aa58d4c 100644
--- a/crates/googletest/src/matchers/str_matcher.rs
+++ b/crates/googletest/src/matchers/str_matcher.rs
@@ -14,16 +14,15 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
matcher_support::{
edit_distance,
summarize_diff::{create_diff, create_diff_reversed},
},
- matchers::{eq_deref_of_matcher::EqDerefOfMatcher, eq_matcher::EqMatcher},
+ matchers::eq_matcher::EqMatcher,
};
use std::borrow::Cow;
use std::fmt::Debug;
-use std::marker::PhantomData;
use std::ops::Deref;
/// Matches a string containing a given substring.
@@ -59,11 +58,10 @@
/// > and expected values when matching strings while
/// > [`ignoring_ascii_case`][StrMatcherConfigurator::ignoring_ascii_case] is
/// > set.
-pub fn contains_substring<A: ?Sized, T>(expected: T) -> StrMatcher<A, T> {
+pub fn contains_substring<T>(expected: T) -> StrMatcher<T> {
StrMatcher {
configuration: Configuration { mode: MatchMode::Contains, ..Default::default() },
expected,
- phantom: Default::default(),
}
}
@@ -99,11 +97,10 @@
///
/// See the [`StrMatcherConfigurator`] extension trait for more options on how
/// the string is matched.
-pub fn starts_with<A: ?Sized, T>(expected: T) -> StrMatcher<A, T> {
+pub fn starts_with<T>(expected: T) -> StrMatcher<T> {
StrMatcher {
configuration: Configuration { mode: MatchMode::StartsWith, ..Default::default() },
expected,
- phantom: Default::default(),
}
}
@@ -139,11 +136,10 @@
///
/// See the [`StrMatcherConfigurator`] extension trait for more options on how
/// the string is matched.
-pub fn ends_with<A: ?Sized, T>(expected: T) -> StrMatcher<A, T> {
+pub fn ends_with<T>(expected: T) -> StrMatcher<T> {
StrMatcher {
configuration: Configuration { mode: MatchMode::EndsWith, ..Default::default() },
expected,
- phantom: Default::default(),
}
}
@@ -152,7 +148,7 @@
/// Matchers which match against string values and, through configuration,
/// specialise to [`StrMatcher`] implement this trait. That includes
/// [`EqMatcher`] and [`StrMatcher`].
-pub trait StrMatcherConfigurator<ActualT: ?Sized, ExpectedT> {
+pub trait StrMatcherConfigurator<ExpectedT> {
/// Configures the matcher to ignore any leading whitespace in either the
/// actual or the expected value.
///
@@ -171,7 +167,7 @@
/// When all other configuration options are left as the defaults, this is
/// equivalent to invoking [`str::trim_start`] on both the expected and
/// actual value.
- fn ignoring_leading_whitespace(self) -> StrMatcher<ActualT, ExpectedT>;
+ fn ignoring_leading_whitespace(self) -> StrMatcher<ExpectedT>;
/// Configures the matcher to ignore any trailing whitespace in either the
/// actual or the expected value.
@@ -191,7 +187,7 @@
/// When all other configuration options are left as the defaults, this is
/// equivalent to invoking [`str::trim_end`] on both the expected and
/// actual value.
- fn ignoring_trailing_whitespace(self) -> StrMatcher<ActualT, ExpectedT>;
+ fn ignoring_trailing_whitespace(self) -> StrMatcher<ExpectedT>;
/// Configures the matcher to ignore both leading and trailing whitespace in
/// either the actual or the expected value.
@@ -215,7 +211,7 @@
/// When all other configuration options are left as the defaults, this is
/// equivalent to invoking [`str::trim`] on both the expected and actual
/// value.
- fn ignoring_outer_whitespace(self) -> StrMatcher<ActualT, ExpectedT>;
+ fn ignoring_outer_whitespace(self) -> StrMatcher<ExpectedT>;
/// Configures the matcher to ignore ASCII case when comparing values.
///
@@ -237,7 +233,7 @@
///
/// This is **not guaranteed** to match strings with differing upper/lower
/// case characters outside of the codepoints 0-127 covered by ASCII.
- fn ignoring_ascii_case(self) -> StrMatcher<ActualT, ExpectedT>;
+ fn ignoring_ascii_case(self) -> StrMatcher<ExpectedT>;
/// Configures the matcher to match only strings which otherwise satisfy the
/// conditions a number times matched by the matcher `times`.
@@ -272,10 +268,7 @@
/// This is only meaningful when the matcher was constructed with
/// [`contains_substring`]. This method will panic when it is used with any
/// other matcher construction.
- fn times(
- self,
- times: impl Matcher<ActualT = usize> + 'static,
- ) -> StrMatcher<ActualT, ExpectedT>;
+ fn times(self, times: impl Matcher<usize> + 'static) -> StrMatcher<ExpectedT>;
}
/// A matcher which matches equality or containment of a string-like value in a
@@ -287,20 +280,18 @@
/// * [`contains_substring`],
/// * [`starts_with`],
/// * [`ends_with`].
-pub struct StrMatcher<ActualT: ?Sized, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct StrMatcher<ExpectedT> {
expected: ExpectedT,
configuration: Configuration,
- phantom: PhantomData<ActualT>,
}
-impl<ExpectedT, ActualT> Matcher for StrMatcher<ActualT, ExpectedT>
+impl<ExpectedT, ActualT> Matcher<ActualT> for StrMatcher<ExpectedT>
where
ExpectedT: Deref<Target = str> + Debug,
- ActualT: AsRef<str> + Debug + ?Sized,
+ ActualT: AsRef<str> + Debug + Copy,
{
- type ActualT = ActualT;
-
- fn matches(&self, actual: &ActualT) -> MatcherResult {
+ fn matches(&self, actual: ActualT) -> MatcherResult {
self.configuration.do_strings_match(self.expected.deref(), actual.as_ref()).into()
}
@@ -308,15 +299,15 @@
self.configuration.describe(matcher_result, self.expected.deref())
}
- fn explain_match(&self, actual: &ActualT) -> Description {
+ fn explain_match(&self, actual: ActualT) -> Description {
self.configuration.explain_match(self.expected.deref(), actual.as_ref())
}
}
-impl<ActualT: ?Sized, ExpectedT, MatcherT: Into<StrMatcher<ActualT, ExpectedT>>>
- StrMatcherConfigurator<ActualT, ExpectedT> for MatcherT
+impl<ExpectedT, MatcherT: Into<StrMatcher<ExpectedT>>> StrMatcherConfigurator<ExpectedT>
+ for MatcherT
{
- fn ignoring_leading_whitespace(self) -> StrMatcher<ActualT, ExpectedT> {
+ fn ignoring_leading_whitespace(self) -> StrMatcher<ExpectedT> {
let existing = self.into();
StrMatcher {
configuration: existing.configuration.ignoring_leading_whitespace(),
@@ -324,7 +315,7 @@
}
}
- fn ignoring_trailing_whitespace(self) -> StrMatcher<ActualT, ExpectedT> {
+ fn ignoring_trailing_whitespace(self) -> StrMatcher<ExpectedT> {
let existing = self.into();
StrMatcher {
configuration: existing.configuration.ignoring_trailing_whitespace(),
@@ -332,20 +323,17 @@
}
}
- fn ignoring_outer_whitespace(self) -> StrMatcher<ActualT, ExpectedT> {
+ fn ignoring_outer_whitespace(self) -> StrMatcher<ExpectedT> {
let existing = self.into();
StrMatcher { configuration: existing.configuration.ignoring_outer_whitespace(), ..existing }
}
- fn ignoring_ascii_case(self) -> StrMatcher<ActualT, ExpectedT> {
+ fn ignoring_ascii_case(self) -> StrMatcher<ExpectedT> {
let existing = self.into();
StrMatcher { configuration: existing.configuration.ignoring_ascii_case(), ..existing }
}
- fn times(
- self,
- times: impl Matcher<ActualT = usize> + 'static,
- ) -> StrMatcher<ActualT, ExpectedT> {
+ fn times(self, times: impl Matcher<usize> + 'static) -> StrMatcher<ExpectedT> {
let existing = self.into();
if !matches!(existing.configuration.mode, MatchMode::Contains) {
panic!("The times() configurator is only meaningful with contains_substring().");
@@ -354,25 +342,19 @@
}
}
-impl<A: ?Sized, T: Deref<Target = str>> From<EqMatcher<A, T>> for StrMatcher<A, T> {
- fn from(value: EqMatcher<A, T>) -> Self {
+impl<T: Deref<Target = str>> From<EqMatcher<T>> for StrMatcher<T> {
+ fn from(value: EqMatcher<T>) -> Self {
Self::with_default_config(value.expected)
}
}
-impl<A: ?Sized, T: Deref<Target = str>> From<EqDerefOfMatcher<A, T>> for StrMatcher<A, T> {
- fn from(value: EqDerefOfMatcher<A, T>) -> Self {
- Self::with_default_config(value.expected)
- }
-}
-
-impl<A: ?Sized, T> StrMatcher<A, T> {
+impl<T> StrMatcher<T> {
/// Returns a [`StrMatcher`] with a default configuration to match against
/// the given expected value.
///
/// This default configuration is sensitive to whitespace and case.
fn with_default_config(expected: T) -> Self {
- Self { expected, configuration: Default::default(), phantom: Default::default() }
+ Self { expected, configuration: Default::default() }
}
}
@@ -387,7 +369,7 @@
ignore_leading_whitespace: bool,
ignore_trailing_whitespace: bool,
case_policy: CasePolicy,
- times: Option<Box<dyn Matcher<ActualT = usize>>>,
+ times: Option<Box<dyn Matcher<usize>>>,
}
#[derive(Clone)]
@@ -461,13 +443,13 @@
// Split returns an iterator over the "boundaries" left and right of the
// substring to be matched, of which there is one more than the number of
// substrings.
- matches!(times.matches(&(actual.split(expected).count() - 1)), MatcherResult::Match)
+ matches!(times.matches(actual.split(expected).count() - 1), MatcherResult::Match)
} else {
actual.contains(expected)
}
}
- // StrMatcher::describe redirects immediately to this function.
+ // StrMatcher::<str>::describe redirects immediately to this function.
fn describe(&self, matcher_result: MatcherResult, expected: &str) -> Description {
let mut addenda: Vec<Cow<'static, str>> = Vec::with_capacity(3);
match (self.ignore_leading_whitespace, self.ignore_trailing_whitespace) {
@@ -551,7 +533,11 @@
MatchMode::EndsWith => create_diff_reversed(actual, expected, self.mode.to_diff_mode()),
};
- format!("{default_explanation}\n{diff}").into()
+ if diff.is_empty() {
+ format!("{default_explanation}").into()
+ } else {
+ format!("{default_explanation}\n\n{diff}").into()
+ }
}
fn ignoring_leading_whitespace(self) -> Self {
@@ -570,7 +556,7 @@
Self { case_policy: CasePolicy::IgnoreAscii, ..self }
}
- fn times(self, times: impl Matcher<ActualT = usize> + 'static) -> Self {
+ fn times(self, times: impl Matcher<usize> + 'static) -> Self {
Self { times: Some(Box::new(times)), ..self }
}
}
@@ -589,8 +575,7 @@
#[cfg(test)]
mod tests {
- use super::{contains_substring, ends_with, starts_with, StrMatcher, StrMatcherConfigurator};
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
@@ -713,23 +698,13 @@
}
#[test]
- fn allows_ignoring_ascii_case_from_eq_deref_of_str_slice() -> Result<()> {
- verify_that!("A string", eq_deref_of("A STRING").ignoring_ascii_case())
- }
-
- #[test]
- fn allows_ignoring_ascii_case_from_eq_deref_of_owned_string() -> Result<()> {
- verify_that!("A string", eq_deref_of("A STRING".to_string()).ignoring_ascii_case())
- }
-
- #[test]
fn matches_string_containing_expected_value_in_contains_mode() -> Result<()> {
verify_that!("Some string", contains_substring("str"))
}
#[test]
- fn matches_string_containing_expected_value_in_contains_mode_while_ignoring_ascii_case()
- -> Result<()> {
+ fn matches_string_containing_expected_value_in_contains_mode_while_ignoring_ascii_case(
+ ) -> Result<()> {
verify_that!("Some string", contains_substring("STR").ignoring_ascii_case())
}
@@ -810,48 +785,45 @@
#[test]
fn describes_itself_for_matching_result() -> Result<()> {
- let matcher: StrMatcher<&str, _> = StrMatcher::with_default_config("A string");
+ let matcher = StrMatcher::with_default_config("A string");
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("is equal to \"A string\""))
)
}
#[test]
fn describes_itself_for_non_matching_result() -> Result<()> {
- let matcher: StrMatcher<&str, _> = StrMatcher::with_default_config("A string");
+ let matcher = StrMatcher::with_default_config("A string");
verify_that!(
- Matcher::describe(&matcher, MatcherResult::NoMatch),
+ Matcher::<&str>::describe(&matcher, MatcherResult::NoMatch),
displays_as(eq("isn't equal to \"A string\""))
)
}
#[test]
fn describes_itself_for_matching_result_ignoring_leading_whitespace() -> Result<()> {
- let matcher: StrMatcher<&str, _> =
- StrMatcher::with_default_config("A string").ignoring_leading_whitespace();
+ let matcher = StrMatcher::with_default_config("A string").ignoring_leading_whitespace();
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("is equal to \"A string\" (ignoring leading whitespace)"))
)
}
#[test]
fn describes_itself_for_non_matching_result_ignoring_leading_whitespace() -> Result<()> {
- let matcher: StrMatcher<&str, _> =
- StrMatcher::with_default_config("A string").ignoring_leading_whitespace();
+ let matcher = StrMatcher::with_default_config("A string").ignoring_leading_whitespace();
verify_that!(
- Matcher::describe(&matcher, MatcherResult::NoMatch),
+ Matcher::<&str>::describe(&matcher, MatcherResult::NoMatch),
displays_as(eq("isn't equal to \"A string\" (ignoring leading whitespace)"))
)
}
#[test]
fn describes_itself_for_matching_result_ignoring_trailing_whitespace() -> Result<()> {
- let matcher: StrMatcher<&str, _> =
- StrMatcher::with_default_config("A string").ignoring_trailing_whitespace();
+ let matcher = StrMatcher::with_default_config("A string").ignoring_trailing_whitespace();
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("is equal to \"A string\" (ignoring trailing whitespace)"))
)
}
@@ -859,32 +831,30 @@
#[test]
fn describes_itself_for_matching_result_ignoring_leading_and_trailing_whitespace() -> Result<()>
{
- let matcher: StrMatcher<&str, _> =
- StrMatcher::with_default_config("A string").ignoring_outer_whitespace();
+ let matcher = StrMatcher::with_default_config("A string").ignoring_outer_whitespace();
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("is equal to \"A string\" (ignoring leading and trailing whitespace)"))
)
}
#[test]
fn describes_itself_for_matching_result_ignoring_ascii_case() -> Result<()> {
- let matcher: StrMatcher<&str, _> =
- StrMatcher::with_default_config("A string").ignoring_ascii_case();
+ let matcher = StrMatcher::with_default_config("A string").ignoring_ascii_case();
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("is equal to \"A string\" (ignoring ASCII case)"))
)
}
#[test]
- fn describes_itself_for_matching_result_ignoring_ascii_case_and_leading_whitespace()
- -> Result<()> {
- let matcher: StrMatcher<&str, _> = StrMatcher::with_default_config("A string")
+ fn describes_itself_for_matching_result_ignoring_ascii_case_and_leading_whitespace(
+ ) -> Result<()> {
+ let matcher = StrMatcher::with_default_config("A string")
.ignoring_leading_whitespace()
.ignoring_ascii_case();
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq(
"is equal to \"A string\" (ignoring leading whitespace, ignoring ASCII case)"
))
@@ -893,63 +863,63 @@
#[test]
fn describes_itself_for_matching_result_in_contains_mode() -> Result<()> {
- let matcher: StrMatcher<&str, _> = contains_substring("A string");
+ let matcher = contains_substring("A string");
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("contains a substring \"A string\""))
)
}
#[test]
fn describes_itself_for_non_matching_result_in_contains_mode() -> Result<()> {
- let matcher: StrMatcher<&str, _> = contains_substring("A string");
+ let matcher = contains_substring("A string");
verify_that!(
- Matcher::describe(&matcher, MatcherResult::NoMatch),
+ Matcher::<&str>::describe(&matcher, MatcherResult::NoMatch),
displays_as(eq("does not contain a substring \"A string\""))
)
}
#[test]
fn describes_itself_with_count_number() -> Result<()> {
- let matcher: StrMatcher<&str, _> = contains_substring("A string").times(gt(2));
+ let matcher = contains_substring("A string").times(gt(2));
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("contains a substring \"A string\" (count is greater than 2)"))
)
}
#[test]
fn describes_itself_for_matching_result_in_starts_with_mode() -> Result<()> {
- let matcher: StrMatcher<&str, _> = starts_with("A string");
+ let matcher = starts_with("A string");
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("starts with prefix \"A string\""))
)
}
#[test]
fn describes_itself_for_non_matching_result_in_starts_with_mode() -> Result<()> {
- let matcher: StrMatcher<&str, _> = starts_with("A string");
+ let matcher = starts_with("A string");
verify_that!(
- Matcher::describe(&matcher, MatcherResult::NoMatch),
+ Matcher::<&str>::describe(&matcher, MatcherResult::NoMatch),
displays_as(eq("does not start with \"A string\""))
)
}
#[test]
fn describes_itself_for_matching_result_in_ends_with_mode() -> Result<()> {
- let matcher: StrMatcher<&str, _> = ends_with("A string");
+ let matcher = ends_with("A string");
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&str>::describe(&matcher, MatcherResult::Match),
displays_as(eq("ends with suffix \"A string\""))
)
}
#[test]
fn describes_itself_for_non_matching_result_in_ends_with_mode() -> Result<()> {
- let matcher: StrMatcher<&str, _> = ends_with("A string");
+ let matcher = ends_with("A string");
verify_that!(
- Matcher::describe(&matcher, MatcherResult::NoMatch),
+ Matcher::<&str>::describe(&matcher, MatcherResult::NoMatch),
displays_as(eq("does not end with \"A string\""))
)
}
@@ -1019,8 +989,8 @@
}
#[test]
- fn match_explanation_for_starts_with_includes_both_versions_of_differing_last_line()
- -> Result<()> {
+ fn match_explanation_for_starts_with_includes_both_versions_of_differing_last_line(
+ ) -> Result<()> {
let result = verify_that!(
indoc!(
"
@@ -1121,8 +1091,8 @@
}
#[test]
- fn match_explanation_for_contains_substring_shows_diff_when_first_and_last_line_are_incomplete()
- -> Result<()> {
+ fn match_explanation_for_contains_substring_shows_diff_when_first_and_last_line_are_incomplete(
+ ) -> Result<()> {
let result = verify_that!(
indoc!(
"
diff --git a/crates/googletest/src/matchers/subset_of_matcher.rs b/crates/googletest/src/matchers/subset_of_matcher.rs
index 24c00d8..2beaf2d 100644
--- a/crates/googletest/src/matchers/subset_of_matcher.rs
+++ b/crates/googletest/src/matchers/subset_of_matcher.rs
@@ -14,9 +14,9 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches a container all of whose items are in the given container
/// `superset`.
@@ -24,23 +24,23 @@
/// The element type `ElementT` must implement `PartialEq` to allow element
/// comparison.
///
-/// `ActualT` and `ExpectedT` can each be any container a reference to which
+/// `ActualT` and `ExpectedT` can each be any container which
/// implements `IntoIterator`. For instance, `T` can be a common container like
-/// `Vec` or arrays. They need not be the same container type.
+/// `&Vec` or arrays. They need not be the same container type.
///
/// ```
/// # use googletest::prelude::*;
/// # use std::collections::HashSet;
/// # fn should_pass_1() -> Result<()> {
/// let value = vec![1, 2, 3];
-/// verify_that!(value, subset_of([1, 2, 3, 4]))?; // Passes
+/// verify_that!(value, subset_of([&1, &2, &3, &4]))?; // Passes
/// let array_value = [1, 2, 3];
/// verify_that!(array_value, subset_of([1, 2, 3, 4]))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// # let value = vec![1, 2, 3];
-/// verify_that!(value, subset_of([1, 2]))?; // Fails: 3 is not in the superset
+/// verify_that!(value, subset_of([&1, &2]))?; // Fails: 3 is not in the superset
/// # Ok(())
/// # }
/// # should_pass_1().unwrap();
@@ -48,7 +48,7 @@
///
/// # fn should_pass_2() -> Result<()> {
/// let value: HashSet<i32> = [1, 2, 3].into();
-/// verify_that!(value, subset_of([1, 2, 3]))?; // Passes
+/// verify_that!(value, subset_of([&1, &2, &3]))?; // Passes
/// # Ok(())
/// # }
/// # should_pass_2().unwrap();
@@ -60,20 +60,8 @@
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let value: Vec<i32> = vec![0, 0, 1];
-/// verify_that!(value, subset_of([0, 1]))?; // Passes
-/// verify_that!(value, subset_of([0, 1, 1]))?; // Passes
-/// # Ok(())
-/// # }
-/// # should_pass().unwrap();
-/// ```
-///
-/// One can also verify the contents of a slice by dereferencing it:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # fn should_pass() -> Result<()> {
-/// let value = &[1, 2, 3];
-/// verify_that!(*value, subset_of([1, 2, 3]))?;
+/// verify_that!(value, subset_of([&0, &1]))?; // Passes
+/// verify_that!(value, subset_of([&0, &1, &1]))?; // Passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
@@ -83,43 +71,35 @@
/// runtime proportional to the *product* of the sizes of the actual and
/// expected containers as well as the time to check equality of each pair of
/// items. It should not be used on especially large containers.
-pub fn subset_of<ElementT: Debug + PartialEq, ActualT: Debug + ?Sized, ExpectedT: Debug>(
- superset: ExpectedT,
-) -> impl Matcher<ActualT = ActualT>
-where
- for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
- for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
-{
- SubsetOfMatcher::<ActualT, _> { superset, phantom: Default::default() }
+pub fn subset_of<ExpectedT>(superset: ExpectedT) -> SubsetOfMatcher<ExpectedT> {
+ SubsetOfMatcher { superset }
}
-struct SubsetOfMatcher<ActualT: ?Sized, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct SubsetOfMatcher<ExpectedT> {
superset: ExpectedT,
- phantom: PhantomData<ActualT>,
}
-impl<ElementT: Debug + PartialEq, ActualT: Debug + ?Sized, ExpectedT: Debug> Matcher
- for SubsetOfMatcher<ActualT, ExpectedT>
+impl<ElementT: Debug + PartialEq + Copy, ActualT: Debug + Copy, ExpectedT: Debug> Matcher<ActualT>
+ for SubsetOfMatcher<ExpectedT>
where
- for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
+ ActualT: IntoIterator<Item = ElementT>,
for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
{
- type ActualT = ActualT;
-
- fn matches(&self, actual: &ActualT) -> MatcherResult {
+ fn matches(&self, actual: ActualT) -> MatcherResult {
for actual_item in actual {
- if self.expected_is_missing(actual_item) {
+ if self.expected_is_missing(&actual_item) {
return MatcherResult::NoMatch;
}
}
MatcherResult::Match
}
- fn explain_match(&self, actual: &ActualT) -> Description {
+ fn explain_match(&self, actual: ActualT) -> Description {
let unexpected_elements = actual
.into_iter()
.enumerate()
- .filter(|&(_, actual_item)| self.expected_is_missing(actual_item))
+ .filter(|item| self.expected_is_missing(&item.1))
.map(|(idx, actual_item)| format!("{actual_item:#?} at #{idx}"))
.collect::<Vec<_>>();
@@ -138,18 +118,17 @@
}
}
-impl<ActualT: ?Sized, ElementT: PartialEq, ExpectedT> SubsetOfMatcher<ActualT, ExpectedT>
+impl<ElementT: PartialEq, ExpectedT> SubsetOfMatcher<ExpectedT>
where
for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
{
fn expected_is_missing(&self, needle: &ElementT) -> bool {
- !self.superset.into_iter().any(|item| *item == *needle)
+ !self.superset.into_iter().any(|item| item == needle)
}
}
#[cfg(test)]
mod tests {
- use super::subset_of;
use crate::prelude::*;
use indoc::indoc;
use std::collections::HashSet;
@@ -163,54 +142,54 @@
#[test]
fn subset_of_matches_vec_with_one_element() -> Result<()> {
let value = vec![1];
- verify_that!(value, subset_of([1]))
+ verify_that!(value, subset_of([&1]))
}
#[test]
fn subset_of_matches_vec_with_two_elements() -> Result<()> {
let value = vec![1, 2];
- verify_that!(value, subset_of([1, 2]))
+ verify_that!(value, subset_of([&1, &2]))
}
#[test]
fn subset_of_matches_vec_when_expected_has_excess_element() -> Result<()> {
let value = vec![1, 2];
- verify_that!(value, subset_of([1, 2, 3]))
+ verify_that!(value, subset_of([&1, &2, &3]))
}
#[test]
fn subset_of_matches_vec_when_expected_has_excess_element_first() -> Result<()> {
let value = vec![1, 2];
- verify_that!(value, subset_of([3, 1, 2]))
+ verify_that!(value, subset_of([&3, &1, &2]))
}
#[test]
fn subset_of_matches_slice_with_one_element() -> Result<()> {
let value = &[1];
- verify_that!(*value, subset_of([1]))
+ verify_that!(value, subset_of([&1]))
}
#[test]
fn subset_of_matches_hash_set_with_one_element() -> Result<()> {
let value: HashSet<i32> = [1].into();
- verify_that!(value, subset_of([1]))
+ verify_that!(value, subset_of([&1]))
}
#[test]
fn subset_of_does_not_match_when_first_element_does_not_match() -> Result<()> {
let value = vec![0];
- verify_that!(value, not(subset_of([1])))
+ verify_that!(value, not(subset_of([&1])))
}
#[test]
fn subset_of_does_not_match_when_second_element_does_not_match() -> Result<()> {
let value = vec![2, 0];
- verify_that!(value, not(subset_of([2])))
+ verify_that!(value, not(subset_of([&2])))
}
#[test]
fn subset_of_shows_correct_message_when_first_item_does_not_match() -> Result<()> {
- let result = verify_that!(vec![0, 2, 3], subset_of([1, 2, 3]));
+ let result = verify_that!(vec![0, 2, 3], subset_of([&1, &2, &3]));
verify_that!(
result,
@@ -231,7 +210,7 @@
#[test]
fn subset_of_shows_correct_message_when_second_item_does_not_match() -> Result<()> {
- let result = verify_that!(vec![1, 0, 3], subset_of([1, 2, 3]));
+ let result = verify_that!(vec![1, 0, 3], subset_of([&1, &2, &3]));
verify_that!(
result,
@@ -252,7 +231,7 @@
#[test]
fn subset_of_shows_correct_message_when_first_two_items_do_not_match() -> Result<()> {
- let result = verify_that!(vec![0, 0, 3], subset_of([1, 2, 3]));
+ let result = verify_that!(vec![0, 0, 3], subset_of([&1, &2, &3]));
verify_that!(
result,
diff --git a/crates/googletest/src/matchers/superset_of_matcher.rs b/crates/googletest/src/matchers/superset_of_matcher.rs
index d1e9d72..8d28e45 100644
--- a/crates/googletest/src/matchers/superset_of_matcher.rs
+++ b/crates/googletest/src/matchers/superset_of_matcher.rs
@@ -14,9 +14,9 @@
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
-use std::{fmt::Debug, marker::PhantomData};
+use std::fmt::Debug;
/// Matches a container containing all of the items in the given container
/// `subset`.
@@ -24,24 +24,24 @@
/// The element type `ElementT` must implement `PartialEq` to allow element
/// comparison.
///
-/// `ActualT` and `ExpectedT` can each be any container a reference to which
-/// implements `IntoIterator`. For instance, `ActualT` and `ExpectedT` can be a
-/// common container like `Vec` or arrays. They need not be the same container
-/// type.
+/// `ActualT` and `ExpectedT` can each be any container which implements
+/// `IntoIterator`. For instance, `ActualT` and `ExpectedT` can be a
+/// common container like `&Vec`, arrays or slices. They need not be the same
+/// container type.
///
/// ```
/// # use googletest::prelude::*;
/// # use std::collections::HashSet;
/// # fn should_pass_1() -> Result<()> {
/// let value = vec![1, 2, 3];
-/// verify_that!(value, superset_of([1, 2]))?; // Passes
+/// verify_that!(value, superset_of([&1, &2]))?; // Passes
/// let array_value = [1, 2, 3];
/// verify_that!(array_value, superset_of([1, 2]))?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail() -> Result<()> {
/// # let value = vec![1, 2, 3];
-/// verify_that!(value, superset_of([1, 2, 4]))?; // Fails: 4 is not in the subset
+/// verify_that!(value, superset_of([&1, &2, &4]))?; // Fails: 4 is not in the subset
/// # Ok(())
/// # }
/// # should_pass_1().unwrap();
@@ -49,7 +49,7 @@
///
/// # fn should_pass_2() -> Result<()> {
/// let value: HashSet<i32> = [1, 2, 3].into();
-/// verify_that!(value, superset_of([1, 2, 3]))?; // Passes
+/// verify_that!(value, superset_of([&1, &2, &3]))?; // Passes
/// # Ok(())
/// # }
/// # should_pass_2().unwrap();
@@ -61,20 +61,8 @@
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
/// let value: Vec<i32> = vec![0, 0, 1];
-/// verify_that!(value, superset_of([0, 1]))?; // Passes
-/// verify_that!(value, superset_of([0, 1, 1]))?; // Passes
-/// # Ok(())
-/// # }
-/// # should_pass().unwrap();
-/// ```
-///
-/// One can also verify the contents of a slice by dereferencing it:
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # fn should_pass() -> Result<()> {
-/// let value = &[1, 2, 3];
-/// verify_that!(*value, superset_of([1, 2, 3]))?;
+/// verify_that!(value, superset_of([&0, &1]))?; // Passes
+/// verify_that!(value, superset_of([&0, &1, &1]))?; // Passes
/// # Ok(())
/// # }
/// # should_pass().unwrap();
@@ -84,30 +72,22 @@
/// runtime proportional to the *product* of the sizes of the actual and
/// expected containers as well as the time to check equality of each pair of
/// items. It should not be used on especially large containers.
-pub fn superset_of<ElementT: Debug + PartialEq, ActualT: Debug + ?Sized, ExpectedT: Debug>(
- subset: ExpectedT,
-) -> impl Matcher<ActualT = ActualT>
-where
- for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
- for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
-{
- SupersetOfMatcher::<ActualT, _> { subset, phantom: Default::default() }
+pub fn superset_of<ExpectedT>(subset: ExpectedT) -> SupersetOfMatcher<ExpectedT> {
+ SupersetOfMatcher { subset }
}
-struct SupersetOfMatcher<ActualT: ?Sized, ExpectedT> {
+#[derive(MatcherBase)]
+pub struct SupersetOfMatcher<ExpectedT> {
subset: ExpectedT,
- phantom: PhantomData<ActualT>,
}
-impl<ElementT: Debug + PartialEq, ActualT: Debug + ?Sized, ExpectedT: Debug> Matcher
- for SupersetOfMatcher<ActualT, ExpectedT>
+impl<ElementT: Debug + Copy + PartialEq, ActualT: Debug + Copy, ExpectedT: Debug> Matcher<ActualT>
+ for SupersetOfMatcher<ExpectedT>
where
- for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
+ ActualT: IntoIterator<Item = ElementT>,
for<'a> &'a ExpectedT: IntoIterator<Item = &'a ElementT>,
{
- type ActualT = ActualT;
-
- fn matches(&self, actual: &ActualT) -> MatcherResult {
+ fn matches(&self, actual: ActualT) -> MatcherResult {
for expected_item in &self.subset {
if actual_is_missing(actual, expected_item) {
return MatcherResult::NoMatch;
@@ -116,11 +96,11 @@
MatcherResult::Match
}
- fn explain_match(&self, actual: &ActualT) -> Description {
+ fn explain_match(&self, actual: ActualT) -> Description {
let missing_items: Vec<_> = self
.subset
.into_iter()
- .filter(|expected_item| actual_is_missing(actual, expected_item))
+ .filter(|expected_item| actual_is_missing(actual, *expected_item))
.map(|expected_item| format!("{expected_item:#?}"))
.collect();
match missing_items.len() {
@@ -138,19 +118,14 @@
}
}
-fn actual_is_missing<ElementT: PartialEq, ActualT: ?Sized>(
- actual: &ActualT,
- needle: &ElementT,
-) -> bool
+fn actual_is_missing<ElementT: PartialEq, ActualT>(actual: ActualT, needle: &ElementT) -> bool
where
- for<'a> &'a ActualT: IntoIterator<Item = &'a ElementT>,
+ ActualT: IntoIterator<Item = ElementT>,
{
- !actual.into_iter().any(|item| *item == *needle)
+ !actual.into_iter().any(|item| &item == needle)
}
-
#[cfg(test)]
mod tests {
- use super::superset_of;
use crate::prelude::*;
use indoc::indoc;
use std::collections::HashSet;
@@ -164,54 +139,54 @@
#[test]
fn superset_of_matches_vec_with_one_element() -> Result<()> {
let value = vec![1];
- verify_that!(value, superset_of([1]))
+ verify_that!(value, superset_of([&1]))
}
#[test]
fn superset_of_matches_vec_with_two_items() -> Result<()> {
let value = vec![1, 2];
- verify_that!(value, superset_of([1, 2]))
+ verify_that!(value, superset_of([&1, &2]))
}
#[test]
fn superset_of_matches_vec_when_actual_has_excess_element() -> Result<()> {
let value = vec![1, 2, 3];
- verify_that!(value, superset_of([1, 2]))
+ verify_that!(value, superset_of([&1, &2]))
}
#[test]
fn superset_of_matches_vec_when_actual_has_excess_element_first() -> Result<()> {
let value = vec![3, 1, 2];
- verify_that!(value, superset_of([1, 2]))
+ verify_that!(value, superset_of([&1, &2]))
}
#[test]
fn superset_of_matches_slice_with_one_element() -> Result<()> {
let value = &[1];
- verify_that!(*value, superset_of([1]))
+ verify_that!(value, superset_of([&1]))
}
#[test]
fn superset_of_matches_hash_set_with_one_element() -> Result<()> {
let value: HashSet<i32> = [1].into();
- verify_that!(value, superset_of([1]))
+ verify_that!(value, superset_of([&1]))
}
#[test]
fn superset_of_does_not_match_when_first_element_does_not_match() -> Result<()> {
let value = vec![0];
- verify_that!(value, not(superset_of([1])))
+ verify_that!(value, not(superset_of([&1])))
}
#[test]
fn superset_of_does_not_match_when_second_element_does_not_match() -> Result<()> {
let value = vec![2];
- verify_that!(value, not(superset_of([2, 0])))
+ verify_that!(value, not(superset_of([&2, &0])))
}
#[test]
fn superset_of_shows_correct_message_when_first_item_does_not_match() -> Result<()> {
- let result = verify_that!(vec![0, 2, 3], superset_of([1, 2, 3]));
+ let result = verify_that!(vec![0, 2, 3], superset_of([&1, &2, &3]));
verify_that!(
result,
@@ -232,7 +207,7 @@
#[test]
fn superset_of_shows_correct_message_when_second_item_does_not_match() -> Result<()> {
- let result = verify_that!(vec![1, 0, 3], superset_of([1, 2, 3]));
+ let result = verify_that!(vec![1, 0, 3], superset_of([&1, &2, &3]));
verify_that!(
result,
@@ -253,7 +228,7 @@
#[test]
fn superset_of_shows_correct_message_when_first_two_items_do_not_match() -> Result<()> {
- let result = verify_that!(vec![0, 0, 3], superset_of([1, 2, 3]));
+ let result = verify_that!(vec![0, 0, 3], superset_of([&1, &2, &3]));
verify_that!(
result,
diff --git a/crates/googletest/src/matchers/tuple_matcher.rs b/crates/googletest/src/matchers/tuple_matcher.rs
index af55cbf..4822d52 100644
--- a/crates/googletest/src/matchers/tuple_matcher.rs
+++ b/crates/googletest/src/matchers/tuple_matcher.rs
@@ -23,16 +23,16 @@
pub mod internal {
use crate::{
description::Description,
- matcher::{Matcher, MatcherResult},
+ matcher::{Matcher, MatcherBase, MatcherResult},
};
use std::fmt::Debug;
+ impl MatcherBase for () {}
+
// This implementation is provided for completeness, but is completely trivial.
// The only actual value which can be supplied is (), which must match.
- impl Matcher for () {
- type ActualT = ();
-
- fn matches(&self, _: &Self::ActualT) -> MatcherResult {
+ impl Matcher<()> for () {
+ fn matches(&self, _: ()) -> MatcherResult {
MatcherResult::Match
}
@@ -44,18 +44,75 @@
}
}
+ impl Matcher<&()> for () {
+ fn matches(&self, _: &()) -> MatcherResult {
+ MatcherResult::Match
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ <Self as Matcher<()>>::describe(self, matcher_result)
+ }
+ }
+
/// Generates a tuple matcher for tuples of a specific length.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
macro_rules! tuple_matcher_n {
($([$field_number:tt, $matcher_type:ident, $field_type:ident]),*) => {
- impl<$($field_type: Debug, $matcher_type: Matcher<ActualT = $field_type>),*>
- Matcher for ($($matcher_type,)*)
- {
- type ActualT = ($($field_type,)*);
+ impl<$($matcher_type: MatcherBase),*> MatcherBase for ($($matcher_type,)*){}
- fn matches(&self, actual: &($($field_type,)*)) -> MatcherResult {
+ impl<$($field_type: Debug + Copy, $matcher_type: Matcher<$field_type>),*>
+ Matcher<($($field_type,)*)> for ($($matcher_type,)*)
+ {
+ fn matches(&self, actual: ($($field_type,)*)) -> MatcherResult {
+ $(match self.$field_number.matches(actual.$field_number) {
+ MatcherResult::Match => {},
+ MatcherResult::NoMatch => {
+ return MatcherResult::NoMatch;
+ }
+ })*
+ MatcherResult::Match
+ }
+
+ fn explain_match(&self, actual: ($($field_type,)*)) -> Description {
+ let mut explanation = Description::new().text("which").nested(
+ self.describe(self.matches(actual)));
+ $(match self.$field_number.matches(actual.$field_number) {
+ MatcherResult::Match => {},
+ MatcherResult::NoMatch => {
+ explanation = explanation
+ .text(format!(concat!("Element #", $field_number, " is {:?},"),
+ actual.$field_number))
+ .nested(self.$field_number.explain_match(actual.$field_number));
+ }
+ })*
+ explanation
+ }
+
+ fn describe(&self, matcher_result: MatcherResult) -> Description {
+ match matcher_result {
+ MatcherResult::Match => {
+ let mut description = Description::new().text(
+ "is a tuple whose values respectively match:");
+ $(description = description.nested(
+ self.$field_number.describe(matcher_result));)*
+ description
+ }
+ MatcherResult::NoMatch => {
+ let mut description = Description::new().text(
+ "is a tuple whose values do not respectively match:");
+ $(description = description.nested(
+ self.$field_number.describe(MatcherResult::Match));)*
+ description
+ }
+ }
+ }
+ }
+ impl<'a, $($field_type: Debug, $matcher_type: Matcher<&'a $field_type>),*>
+ Matcher<&'a ($($field_type,)*)> for ($($matcher_type,)*)
+ {
+ fn matches(&self, actual: &'a ($($field_type,)*)) -> MatcherResult {
$(match self.$field_number.matches(&actual.$field_number) {
MatcherResult::Match => {},
MatcherResult::NoMatch => {
@@ -65,13 +122,22 @@
MatcherResult::Match
}
- fn explain_match(&self, actual: &($($field_type,)*)) -> Description {
- let mut explanation = Description::new().text("which").nested(self.describe(self.matches(actual)));
+ fn explain_match(&self, actual: &'a ($($field_type,)*)) -> Description {
+ let mut explanation = Description::new()
+ .text("which")
+ .nested(
+ Matcher::<&'a ($($field_type,)*)>::describe(
+ self, self.matches(actual)));
$(match self.$field_number.matches(&actual.$field_number) {
MatcherResult::Match => {},
MatcherResult::NoMatch => {
explanation = explanation
- .text(format!(concat!("Element #", $field_number, " is {:?},"), actual.$field_number))
+ .text(format!(
+ concat!(
+ "Element #",
+ $field_number,
+ " is {:?},"),
+ actual.$field_number))
.nested(self.$field_number.explain_match(&actual.$field_number));
}
})*
@@ -81,13 +147,17 @@
fn describe(&self, matcher_result: MatcherResult) -> Description {
match matcher_result {
MatcherResult::Match => {
- let mut description = Description::new().text("is a tuple whose values respectively match:");
- $(description = description.nested(self.$field_number.describe(matcher_result));)*
+ let mut description = Description::new().text(
+ "is a tuple whose values respectively match:");
+ $(description = description.nested(
+ self.$field_number.describe(matcher_result));)*
description
}
MatcherResult::NoMatch => {
- let mut description = Description::new().text("is a tuple whose values do not respectively match:");
- $(description = description.nested(self.$field_number.describe(MatcherResult::Match));)*
+ let mut description = Description::new().text(
+ "is a tuple whose values do not respectively match:");
+ $(description = description.nested(
+ self.$field_number.describe(MatcherResult::Match));)*
description
}
}
diff --git a/crates/googletest/src/matchers/unordered_elements_are_matcher.rs b/crates/googletest/src/matchers/unordered_elements_are_matcher.rs
index f4585a4..50951d7 100644
--- a/crates/googletest/src/matchers/unordered_elements_are_matcher.rs
+++ b/crates/googletest/src/matchers/unordered_elements_are_matcher.rs
@@ -22,19 +22,19 @@
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
-/// verify_that!(vec![3, 2, 1], unordered_elements_are![eq(1), ge(2), anything()])?; // Passes
+/// verify_that!(vec![3, 2, 1], unordered_elements_are![eq(&1), ge(&2), anything()])?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
-/// verify_that!(vec![1], unordered_elements_are![eq(1), ge(2)])?; // Fails: container has wrong size
+/// verify_that!(vec![1], unordered_elements_are![eq(&1), ge(&2)])?; // Fails: container has wrong size
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
-/// verify_that!(vec![3, 2, 1], unordered_elements_are![eq(1), ge(4), eq(2)])?; // Fails: second matcher not matched
+/// verify_that!(vec![3, 2, 1], unordered_elements_are![eq(&1), ge(&4), eq(&2)])?; // Fails: second matcher not matched
/// # Ok(())
/// # }
/// # fn should_fail_3() -> Result<()> {
-/// verify_that!(vec![3, 2, 1], unordered_elements_are![ge(3), ge(3), ge(3)])?; // Fails: no 1:1 correspondence
+/// verify_that!(vec![3, 2, 1], unordered_elements_are![ge(&3), ge(&3), ge(&3)])?; // Fails: no 1:1 correspondence
/// # Ok(())
/// # }
/// # should_pass().unwrap();
@@ -43,32 +43,15 @@
/// # should_fail_3().unwrap_err();
/// ```
///
-/// The actual value must be a container such as a `Vec`, an array, or a
-/// dereferenced slice. More precisely, a shared borrow of the actual value must
-/// implement [`IntoIterator`].
-///
-/// This can also match against [`HashMap`][std::collections::HashMap] and
-/// similar collections. The arguments are a sequence of pairs of matchers
-/// corresponding to the keys and their respective values.
-///
-/// ```
-/// # use googletest::prelude::*;
-/// # use std::collections::HashMap;
-/// let value: HashMap<u32, &'static str> =
-/// HashMap::from_iter([(1, "One"), (2, "Two"), (3, "Three")]);
-/// verify_that!(
-/// value,
-/// unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))]
-/// )
-/// # .unwrap();
-/// ```
+/// The actual value must be a container such as a `&Vec`, an array, or a
+/// slice. More precisely, the actual value must implement [`IntoIterator`].
///
/// This can also be omitted in [`verify_that!`] macros and replaced with curly
/// brackets.
///
/// ```
/// # use googletest::prelude::*;
-/// verify_that!(vec![1, 2], {eq(2), eq(1)})
+/// verify_that!(vec![1, 2], {eq(&2), eq(&1)})
/// # .unwrap();
/// ```
///
@@ -86,12 +69,18 @@
/// ```
/// # use googletest::prelude::*;
/// verify_that!(vec![vec![1,2], vec![3]],
-/// {unordered_elements_are![eq(2), eq(1)], unordered_elements_are![eq(3)]})
+/// {unordered_elements_are![eq(&2), eq(&1)], unordered_elements_are![eq(&3)]})
/// # .unwrap();
/// ```
///
-/// This matcher does not support matching directly against an [`Iterator`]. To
-/// match against an iterator, use [`Iterator::collect`] to build a [`Vec`].
+/// If an inner matcher is `eq(...)`, it can be omitted:
+///
+/// ```
+/// # use googletest::prelude::*;
+///
+/// verify_that!(vec![1,2,3], unordered_elements_are![lt(&2), gt(&1), &3])
+/// # .unwrap();
+/// ```
///
/// The matcher proceeds in three stages:
///
@@ -119,29 +108,23 @@
#[doc(hidden)]
macro_rules! __unordered_elements_are {
($(,)?) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
- UnorderedElementsAreMatcher, Requirements
- };
- UnorderedElementsAreMatcher::new([], Requirements::PerfectMatch)
- }};
-
- // TODO: Consider an alternative map-like syntax here similar to that used in
- // https://crates.io/crates/maplit.
- ($(($key_matcher:expr, $value_matcher:expr)),* $(,)?) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
- UnorderedElementsOfMapAreMatcher, Requirements
- };
- UnorderedElementsOfMapAreMatcher::new(
- [$((Box::new($key_matcher), Box::new($value_matcher))),*],
- Requirements::PerfectMatch
- )
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::
+ UnorderedElementsAreMatcher::new(
+ [],
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::
+ Requirements::PerfectMatch)
}};
($($matcher:expr),* $(,)?) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
- UnorderedElementsAreMatcher, Requirements
- };
- UnorderedElementsAreMatcher::new([$(Box::new($matcher)),*], Requirements::PerfectMatch)
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::
+ UnorderedElementsAreMatcher::new(
+ [$(Box::new(
+ $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
+ $matcher
+ )
+ )),*],
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::
+ Requirements::PerfectMatch)
}};
}
@@ -160,20 +143,20 @@
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
-/// verify_that!(vec![3, 2, 1], contains_each![eq(2), ge(3)])?; // Passes
-/// verify_that!(vec![3, 2, 1], contains_each![ge(2), ge(2)])?; // Passes
+/// verify_that!(vec![3, 2, 1], contains_each![eq(&2), ge(&3)])?; // Passes
+/// verify_that!(vec![3, 2, 1], contains_each![ge(&2), ge(&2)])?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
-/// verify_that!(vec![1], contains_each![eq(1), ge(2)])?; // Fails: container too small
+/// verify_that!(vec![1], contains_each![eq(&1), ge(&2)])?; // Fails: container too small
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
-/// verify_that!(vec![3, 2, 1], contains_each![eq(1), ge(4)])?; // Fails: second matcher unmatched
+/// verify_that!(vec![3, 2, 1], contains_each![eq(&1), ge(&4)])?; // Fails: second matcher unmatched
/// # Ok(())
/// # }
/// # fn should_fail_3() -> Result<()> {
-/// verify_that!(vec![3, 2, 1], contains_each![ge(3), ge(3), ge(3)])?; // Fails: no matching
+/// verify_that!(vec![3, 2, 1], contains_each![ge(&3), ge(&3), ge(&3)])?; // Fails: no matching
/// # Ok(())
/// # }
/// # should_pass().unwrap();
@@ -182,26 +165,18 @@
/// # should_fail_3().unwrap_err();
/// ```
///
-/// The actual value must be a container such as a `Vec`, an array, or a
-/// dereferenced slice. More precisely, a shared borrow of the actual value must
-/// implement [`IntoIterator`].
+/// The actual value must be a container such as a `&Vec`, an array, or a
+/// slice. More precisely, the actual value must implement [`IntoIterator`].
///
-/// This can also match against [`HashMap`][std::collections::HashMap] and
-/// similar collections. The arguments are a sequence of pairs of matchers
-/// corresponding to the keys and their respective values.
+/// If an inner matcher is `eq(...)`, it can be omitted:
///
/// ```
/// # use googletest::prelude::*;
-/// # use std::collections::HashMap;
-/// let value: HashMap<u32, &'static str> =
-/// HashMap::from_iter([(1, "One"), (2, "Two"), (3, "Three")]);
-/// verify_that!(value, contains_each![(eq(2), eq("Two")), (eq(1), eq("One"))])
+///
+/// verify_that!(vec![1,2,3], contains_each![lt(&2), &3])
/// # .unwrap();
/// ```
///
-/// This matcher does not support matching directly against an [`Iterator`]. To
-/// match against an iterator, use [`Iterator::collect`] to build a [`Vec`].
-///
/// The matcher proceeds in three stages:
///
/// 1. It first checks whether the actual value is large enough to possibly be
@@ -225,29 +200,21 @@
#[doc(hidden)]
macro_rules! __contains_each {
($(,)?) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
- UnorderedElementsAreMatcher, Requirements
- };
- UnorderedElementsAreMatcher::new([], Requirements::Superset)
- }};
-
- // TODO: Consider an alternative map-like syntax here similar to that used in
- // https://crates.io/crates/maplit.
- ($(($key_matcher:expr, $value_matcher:expr)),* $(,)?) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
- UnorderedElementsOfMapAreMatcher, Requirements
- };
- UnorderedElementsOfMapAreMatcher::new(
- [$((Box::new($key_matcher), Box::new($value_matcher))),*],
- Requirements::Superset
- )
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::
+ UnorderedElementsAreMatcher::new(
+ [],
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Superset)
}};
($($matcher:expr),* $(,)?) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
- UnorderedElementsAreMatcher, Requirements
- };
- UnorderedElementsAreMatcher::new([$(Box::new($matcher)),*], Requirements::Superset)
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::
+ UnorderedElementsAreMatcher::new(
+ [$(Box::new(
+ $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
+ $matcher
+ )
+ )),*],
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Superset)
}}
}
@@ -267,20 +234,20 @@
/// ```
/// # use googletest::prelude::*;
/// # fn should_pass() -> Result<()> {
-/// verify_that!(vec![2, 1], is_contained_in![eq(1), ge(2)])?; // Passes
-/// verify_that!(vec![2, 1], is_contained_in![ge(1), ge(1)])?; // Passes
+/// verify_that!(vec![2, 1], is_contained_in![eq(&1), ge(&2)])?; // Passes
+/// verify_that!(vec![2, 1], is_contained_in![ge(&1), ge(&1)])?; // Passes
/// # Ok(())
/// # }
/// # fn should_fail_1() -> Result<()> {
-/// verify_that!(vec![1, 2, 3], is_contained_in![eq(1), ge(2)])?; // Fails: container too large
+/// verify_that!(vec![1, 2, 3], is_contained_in![eq(&1), ge(&2)])?; // Fails: container too large
/// # Ok(())
/// # }
/// # fn should_fail_2() -> Result<()> {
-/// verify_that!(vec![2, 1], is_contained_in![eq(1), ge(4)])?; // Fails: second matcher unmatched
+/// verify_that!(vec![2, 1], is_contained_in![eq(&1), ge(&4)])?; // Fails: second matcher unmatched
/// # Ok(())
/// # }
/// # fn should_fail_3() -> Result<()> {
-/// verify_that!(vec![3, 1], is_contained_in![ge(3), ge(3), ge(3)])?; // Fails: no matching
+/// verify_that!(vec![3, 1], is_contained_in![ge(&3), ge(&3), ge(&3)])?; // Fails: no matching
/// # Ok(())
/// # }
/// # should_pass().unwrap();
@@ -289,28 +256,18 @@
/// # should_fail_3().unwrap_err();
/// ```
///
-/// The actual value must be a container such as a `Vec`, an array, or a
-/// dereferenced slice. More precisely, a shared borrow of the actual value must
-/// implement [`IntoIterator`].
+/// The actual value must be a container such as a `&Vec`, an array, or a slice.
+/// More precisely, the actual value must implement [`IntoIterator`].
///
-/// This can also match against [`HashMap`][std::collections::HashMap] and
-/// similar collections. The arguments are a sequence of pairs of matchers
-/// corresponding to the keys and their respective values.
+/// If an inner matcher is `eq(...)`, it can be omitted:
///
/// ```
/// # use googletest::prelude::*;
-/// # use std::collections::HashMap;
-/// let value: HashMap<u32, &'static str> = HashMap::from_iter([(1, "One"), (2, "Two")]);
-/// verify_that!(
-/// value,
-/// is_contained_in![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))]
-/// )
+///
+/// verify_that!(vec![1,2,3], is_contained_in![lt(&2), &3, &4, gt(&0)])
/// # .unwrap();
/// ```
///
-/// This matcher does not support matching directly against an [`Iterator`]. To
-/// match against an iterator, use [`Iterator::collect`] to build a [`Vec`].
-///
/// The matcher proceeds in three stages:
///
/// 1. It first checks whether the actual value is too large to possibly be
@@ -334,29 +291,20 @@
#[doc(hidden)]
macro_rules! __is_contained_in {
($(,)?) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
- UnorderedElementsAreMatcher, Requirements
- };
- UnorderedElementsAreMatcher::new([], Requirements::Subset)
- }};
-
- // TODO: Consider an alternative map-like syntax here similar to that used in
- // https://crates.io/crates/maplit.
- ($(($key_matcher:expr, $value_matcher:expr)),* $(,)?) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
- UnorderedElementsOfMapAreMatcher, Requirements
- };
- UnorderedElementsOfMapAreMatcher::new(
- [$((Box::new($key_matcher), Box::new($value_matcher))),*],
- Requirements::Subset
- )
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::
+ UnorderedElementsAreMatcher::new(
+ [], $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Subset)
}};
($($matcher:expr),* $(,)?) => {{
- use $crate::matchers::__internal_unstable_do_not_depend_on_these::{
- UnorderedElementsAreMatcher, Requirements
- };
- UnorderedElementsAreMatcher::new([$(Box::new($matcher)),*], Requirements::Subset)
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::
+ UnorderedElementsAreMatcher::new(
+ [$(Box::new(
+ $crate::matcher_support::__internal_unstable_do_not_depend_on_these::auto_eq!(
+ $matcher
+ )
+ )),*],
+ $crate::matchers::__internal_unstable_do_not_depend_on_these::Requirements::Subset)
}}
}
@@ -366,31 +314,25 @@
#[doc(hidden)]
pub mod internal {
use crate::description::Description;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::{Matcher, MatcherBase, MatcherResult};
use crate::matcher_support::count_elements::count_elements;
use std::collections::HashSet;
use std::fmt::{Debug, Display};
- use std::marker::PhantomData;
/// This struct is meant to be used only through the
/// `unordered_elements_are![...]` macro.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
- pub struct UnorderedElementsAreMatcher<'a, ContainerT: ?Sized, T: Debug, const N: usize> {
- elements: [Box<dyn Matcher<ActualT = T> + 'a>; N],
+ #[derive(MatcherBase)]
+ pub struct UnorderedElementsAreMatcher<'a, T: Debug + Copy, const N: usize> {
+ elements: [Box<dyn Matcher<T> + 'a>; N],
requirements: Requirements,
- phantom: PhantomData<ContainerT>,
}
- impl<'a, ContainerT: ?Sized, T: Debug, const N: usize>
- UnorderedElementsAreMatcher<'a, ContainerT, T, N>
- {
- pub fn new(
- elements: [Box<dyn Matcher<ActualT = T> + 'a>; N],
- requirements: Requirements,
- ) -> Self {
- Self { elements, requirements, phantom: Default::default() }
+ impl<'a, T: Debug + Copy, const N: usize> UnorderedElementsAreMatcher<'a, T, N> {
+ pub fn new(elements: [Box<dyn Matcher<T> + 'a>; N], requirements: Requirements) -> Self {
+ Self { elements, requirements }
}
}
@@ -403,19 +345,17 @@
// least one expected element and vice versa.
// 3. `UnorderedElementsAreMatcher` verifies that a perfect matching exists
// using Ford-Fulkerson.
- impl<'a, T: Debug, ContainerT: Debug + ?Sized, const N: usize> Matcher
- for UnorderedElementsAreMatcher<'a, ContainerT, T, N>
+ impl<'a, T: Debug + Copy, ContainerT: Debug + Copy, const N: usize> Matcher<ContainerT>
+ for UnorderedElementsAreMatcher<'a, T, N>
where
- for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
+ ContainerT: IntoIterator<Item = T>,
{
- type ActualT = ContainerT;
-
- fn matches(&self, actual: &ContainerT) -> MatcherResult {
+ fn matches(&self, actual: ContainerT) -> MatcherResult {
let match_matrix = MatchMatrix::generate(actual, &self.elements);
match_matrix.is_match_for(self.requirements).into()
}
- fn explain_match(&self, actual: &ContainerT) -> Description {
+ fn explain_match(&self, actual: ContainerT) -> Description {
if let Some(size_mismatch_explanation) =
self.requirements.explain_size_mismatch(actual, N)
{
@@ -450,89 +390,8 @@
}
}
- type KeyValueMatcher<'a, KeyT, ValueT> =
- (Box<dyn Matcher<ActualT = KeyT> + 'a>, Box<dyn Matcher<ActualT = ValueT> + 'a>);
-
- /// This is the analogue to [UnorderedElementsAreMatcher] for maps and
- /// map-like collections.
- ///
- /// **For internal use only. API stablility is not guaranteed!**
- #[doc(hidden)]
- pub struct UnorderedElementsOfMapAreMatcher<'a, ContainerT, KeyT, ValueT, const N: usize>
- where
- ContainerT: ?Sized,
- KeyT: Debug,
- ValueT: Debug,
- {
- elements: [KeyValueMatcher<'a, KeyT, ValueT>; N],
- requirements: Requirements,
- phantom: PhantomData<ContainerT>,
- }
-
- impl<'a, ContainerT, KeyT: Debug, ValueT: Debug, const N: usize>
- UnorderedElementsOfMapAreMatcher<'a, ContainerT, KeyT, ValueT, N>
- {
- pub fn new(
- elements: [KeyValueMatcher<'a, KeyT, ValueT>; N],
- requirements: Requirements,
- ) -> Self {
- Self { elements, requirements, phantom: Default::default() }
- }
- }
-
- impl<'a, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized, const N: usize> Matcher
- for UnorderedElementsOfMapAreMatcher<'a, ContainerT, KeyT, ValueT, N>
- where
- for<'b> &'b ContainerT: IntoIterator<Item = (&'b KeyT, &'b ValueT)>,
- {
- type ActualT = ContainerT;
-
- fn matches(&self, actual: &ContainerT) -> MatcherResult {
- let match_matrix = MatchMatrix::generate_for_map(actual, &self.elements);
- match_matrix.is_match_for(self.requirements).into()
- }
-
- fn explain_match(&self, actual: &ContainerT) -> Description {
- if let Some(size_mismatch_explanation) =
- self.requirements.explain_size_mismatch(actual, N)
- {
- return size_mismatch_explanation;
- }
-
- let match_matrix = MatchMatrix::generate_for_map(actual, &self.elements);
- if let Some(unmatchable_explanation) =
- match_matrix.explain_unmatchable(self.requirements)
- {
- return unmatchable_explanation;
- }
-
- let best_match = match_matrix.find_best_match();
-
- best_match
- .get_explanation_for_map(actual, &self.elements, self.requirements)
- .unwrap_or("whose elements all match".into())
- }
-
- fn describe(&self, matcher_result: MatcherResult) -> Description {
- format!(
- "{} elements matching in any order:\n{}",
- if matcher_result.into() { "contains" } else { "doesn't contain" },
- self.elements
- .iter()
- .map(|(key_matcher, value_matcher)| format!(
- "{} => {}",
- key_matcher.describe(MatcherResult::Match),
- value_matcher.describe(MatcherResult::Match)
- ))
- .collect::<Description>()
- .indent()
- )
- .into()
- }
- }
-
/// The requirements of the mapping between matchers and actual values by
- /// which [`UnorderedElemetnsAre`] is deemed to match its input.
+ /// which [`UnorderedElementsAre`] is deemed to match its input.
///
/// **For internal use only. API stablility is not guaranteed!**
#[doc(hidden)]
@@ -552,14 +411,11 @@
}
impl Requirements {
- fn explain_size_mismatch<ContainerT: ?Sized>(
+ fn explain_size_mismatch<ContainerT: IntoIterator + Copy>(
&self,
- actual: &ContainerT,
+ actual: ContainerT,
expected_size: usize,
- ) -> Option<Description>
- where
- for<'b> &'b ContainerT: IntoIterator,
- {
+ ) -> Option<Description> {
let actual_size = count_elements(actual);
match self {
Requirements::PerfectMatch if actual_size != expected_size => Some(
@@ -601,13 +457,10 @@
struct MatchMatrix<const N: usize>(Vec<[MatcherResult; N]>);
impl<const N: usize> MatchMatrix<N> {
- fn generate<'a, T: Debug + 'a, ContainerT: Debug + ?Sized>(
- actual: &ContainerT,
- expected: &[Box<dyn Matcher<ActualT = T> + 'a>; N],
- ) -> Self
- where
- for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
- {
+ fn generate<'a, T: Debug + Copy + 'a, ContainerT: Debug + Copy + IntoIterator<Item = T>>(
+ actual: ContainerT,
+ expected: &[Box<dyn Matcher<T> + 'a>; N],
+ ) -> Self {
let mut matrix = MatchMatrix(vec![[MatcherResult::NoMatch; N]; count_elements(actual)]);
for (actual_idx, actual) in actual.into_iter().enumerate() {
for (expected_idx, expected) in expected.iter().enumerate() {
@@ -617,24 +470,6 @@
matrix
}
- fn generate_for_map<'a, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized>(
- actual: &ContainerT,
- expected: &[KeyValueMatcher<'a, KeyT, ValueT>; N],
- ) -> Self
- where
- for<'b> &'b ContainerT: IntoIterator<Item = (&'b KeyT, &'b ValueT)>,
- {
- let mut matrix = MatchMatrix(vec![[MatcherResult::NoMatch; N]; count_elements(actual)]);
- for (actual_idx, (actual_key, actual_value)) in actual.into_iter().enumerate() {
- for (expected_idx, (expected_key, expected_value)) in expected.iter().enumerate() {
- matrix.0[actual_idx][expected_idx] = (expected_key.matches(actual_key).into()
- && expected_value.matches(actual_value).into())
- .into();
- }
- }
- matrix
- }
-
fn is_match_for(&self, requirements: Requirements) -> bool {
match requirements {
Requirements::PerfectMatch => {
@@ -959,15 +794,16 @@
(0..N).filter(|expected_idx| !matched_expected.contains(expected_idx)).collect()
}
- fn get_explanation<'a, T: Debug, ContainerT: Debug + ?Sized>(
+ fn get_explanation<
+ 'a,
+ T: Debug + Copy,
+ ContainerT: Debug + Copy + IntoIterator<Item = T>,
+ >(
&self,
- actual: &ContainerT,
- expected: &[Box<dyn Matcher<ActualT = T> + 'a>; N],
+ actual: ContainerT,
+ expected: &[Box<dyn Matcher<T> + 'a>; N],
requirements: Requirements,
- ) -> Option<Description>
- where
- for<'b> &'b ContainerT: IntoIterator<Item = &'b T>,
- {
+ ) -> Option<Description> {
let actual: Vec<_> = actual.into_iter().collect();
if self.is_full_match() {
return None;
@@ -1005,71 +841,12 @@
"which does not have a {requirements} match with the expected elements. The best match found was:\n{best_match}"
).into())
}
-
- fn get_explanation_for_map<'a, KeyT: Debug, ValueT: Debug, ContainerT: Debug + ?Sized>(
- &self,
- actual: &ContainerT,
- expected: &[KeyValueMatcher<'a, KeyT, ValueT>; N],
- requirements: Requirements,
- ) -> Option<Description>
- where
- for<'b> &'b ContainerT: IntoIterator<Item = (&'b KeyT, &'b ValueT)>,
- {
- let actual: Vec<_> = actual.into_iter().collect();
- if self.is_full_match() {
- return None;
- }
- let mut error_message =
- format!("which does not have a {requirements} match with the expected elements.");
-
- error_message.push_str("\n The best match found was: ");
-
- let matches = self.get_matches()
- .map(|(actual_idx, expected_idx)| {
- format!(
- "Actual element {:?} => {:?} at index {actual_idx} matched expected element `{}` => `{}` at index {expected_idx}.",
- actual[actual_idx].0,
- actual[actual_idx].1,
- expected[expected_idx].0.describe(MatcherResult::Match),
- expected[expected_idx].1.describe(MatcherResult::Match),
- )
- });
-
- let unmatched_actual = self.get_unmatched_actual()
- .map(|actual_idx| {
- format!(
- "Actual element {:#?} => {:#?} at index {actual_idx} did not match any remaining expected element.",
- actual[actual_idx].0,
- actual[actual_idx].1,
- )
- });
-
- let unmatched_expected = self.get_unmatched_expected()
- .into_iter()
- .map(|expected_idx| {
- format!(
- "Expected element `{}` => `{}` at index {expected_idx} did not match any remaining actual element.",
- expected[expected_idx].0.describe(MatcherResult::Match),
- expected[expected_idx].1.describe(MatcherResult::Match),
- )
- });
-
- let best_match = matches
- .chain(unmatched_actual)
- .chain(unmatched_expected)
- .collect::<Description>()
- .indent();
- Some(format!(
- "which does not have a {requirements} match with the expected elements. The best match found was:\n{best_match}"
- ).into())
- }
}
}
#[cfg(test)]
mod tests {
- use super::internal::UnorderedElementsOfMapAreMatcher;
- use crate::matcher::{Matcher, MatcherResult};
+ use crate::matcher::MatcherResult;
use crate::prelude::*;
use indoc::indoc;
use std::collections::HashMap;
@@ -1081,20 +858,26 @@
// compiler takes care of that, but when the matcher is created separately,
// we must create the constitute matchers separately so that they
// aren't dropped too early.
- let matchers = ((eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three")));
- let matcher: UnorderedElementsOfMapAreMatcher<HashMap<i32, &str>, _, _, 3> = unordered_elements_are![
- (matchers.0.0, matchers.0.1),
- (matchers.1.0, matchers.1.1),
- (matchers.2.0, matchers.2.1)
+ let matchers = ((eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three")));
+ let matcher = unordered_elements_are![
+ (matchers.0 .0, matchers.0 .1),
+ (matchers.1 .0, matchers.1 .1),
+ (matchers.2 .0, matchers.2 .1)
];
verify_that!(
- Matcher::describe(&matcher, MatcherResult::Match),
+ Matcher::<&HashMap<i32, String>>::describe(&matcher, MatcherResult::Match),
displays_as(eq(indoc!(
"
contains elements matching in any order:
- is equal to 2 => is equal to \"Two\"
- is equal to 1 => is equal to \"One\"
- is equal to 3 => is equal to \"Three\""
+ 0. is a tuple whose values respectively match:
+ is equal to 2
+ is equal to \"Two\"
+ 1. is a tuple whose values respectively match:
+ is equal to 1
+ is equal to \"One\"
+ 2. is a tuple whose values respectively match:
+ is equal to 3
+ is equal to \"Three\""
)))
)
}
@@ -1106,22 +889,26 @@
// compiler takes care of that, but when the matcher is created separately,
// we must create the constitute matchers separately so that they
// aren't dropped too early.
- let matchers = ((anything(), eq(1)), (anything(), eq(2)), (anything(), eq(2)));
- let matcher: UnorderedElementsOfMapAreMatcher<HashMap<u32, u32>, _, _, 3> = unordered_elements_are![
- (matchers.0.0, matchers.0.1),
- (matchers.1.0, matchers.1.1),
- (matchers.2.0, matchers.2.1),
- ];
let value: HashMap<u32, u32> = HashMap::from_iter([(0, 1), (1, 1), (2, 2)]);
+ let matchers = ((anything(), eq(&1)), (anything(), eq(&2)), (anything(), eq(&2)));
+ let matcher = unordered_elements_are![
+ (matchers.0 .0, matchers.0 .1),
+ (matchers.1 .0, matchers.1 .1),
+ (matchers.2 .0, matchers.2 .1),
+ ];
verify_that!(
matcher.explain_match(&value),
- displays_as(contains_regex(
- "Actual element 2 => 2 at index [0-2] matched expected element `is anything` => `is equal to 2` at index [0-2]."
- )).and(displays_as(contains_regex(
- "Actual element [0-1] => [0-1] at index [0-2] did not match any remaining expected element."
- ))).and(displays_as(contains_substring(
- "Expected element `is anything` => `is equal to 2` at index 2 did not match any remaining actual element."
- )))
+ all![
+ displays_as(contains_regex(
+ "Actual element \\(2, 2\\) at index [0-2] matched expected element `is a tuple whose values respectively match:\n is anything\n is equal to 2` at index [0-2]."
+ )),
+ displays_as(contains_regex(
+ "Actual element \\(\n [0-1],\n [0-1],\n \\) at index [0-2] did not match any remaining expected element."
+ )),
+ displays_as(contains_substring(
+ "Expected element `is a tuple whose values respectively match:\n is anything\n is equal to 2` at index 2 did not match any remaining actual element."
+ ))
+ ]
)
}
}
diff --git a/crates/googletest/tests/all_matcher_test.rs b/crates/googletest/tests/all_matcher_test.rs
index 8b36fc0..03f645a 100644
--- a/crates/googletest/tests/all_matcher_test.rs
+++ b/crates/googletest/tests/all_matcher_test.rs
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use googletest::matcher::Matcher;
use googletest::prelude::*;
use indoc::indoc;
@@ -54,7 +53,7 @@
#[derive(Debug, PartialEq)]
struct AStruct(i32);
let expected_value = AStruct(123);
- verify_that!(AStruct(123), all![eq_deref_of(&expected_value)])
+ verify_that!(AStruct(123), all![eq(&expected_value)])
}
#[test]
@@ -115,7 +114,7 @@
#[test]
fn formats_error_message_correctly_when_all_is_inside_ok() -> Result<()> {
let value: std::result::Result<i32, std::io::Error> = Ok(4);
- let result = verify_that!(value, ok(all![eq(1), eq(2), eq(3)]));
+ let result = verify_that!(value, ok(all![eq(&1), eq(&2), eq(&3)]));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
diff --git a/crates/googletest/tests/any_matcher_test.rs b/crates/googletest/tests/any_matcher_test.rs
index 82ed046..425e99c 100644
--- a/crates/googletest/tests/any_matcher_test.rs
+++ b/crates/googletest/tests/any_matcher_test.rs
@@ -12,16 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use googletest::matcher::Matcher;
use googletest::prelude::*;
use indoc::indoc;
#[test]
-fn does_not_match_value_when_list_is_empty() -> Result<()> {
- verify_that!((), not(any!()))
-}
-
-#[test]
fn matches_value_with_single_matching_component() -> Result<()> {
verify_that!(123, any!(eq(123)))
}
@@ -54,7 +48,7 @@
#[derive(Debug, PartialEq)]
struct AStruct(i32);
let expected_value = AStruct(123);
- verify_that!(AStruct(123), any![eq_deref_of(&expected_value)])
+ verify_that!(AStruct(123), any![eq(&expected_value)])
}
#[test]
@@ -66,11 +60,6 @@
}
#[test]
-fn mismatch_description_empty_matcher() -> Result<()> {
- verify_that!(any!().explain_match("Three"), displays_as(eq("which never matches")))
-}
-
-#[test]
fn all_multiple_failed_assertions() -> Result<()> {
let result = verify_that!(4, any![eq(1), eq(2), eq(3)]);
verify_that!(
@@ -115,7 +104,7 @@
#[test]
fn formats_error_message_correctly_when_any_is_inside_ok() -> Result<()> {
let value: std::result::Result<i32, std::io::Error> = Ok(4);
- let result = verify_that!(value, ok(any![eq(1), eq(2), eq(3)]));
+ let result = verify_that!(value, ok(any![eq(&1), eq(&2), eq(&3)]));
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
diff --git a/crates/googletest/tests/assertions_test.rs b/crates/googletest/tests/assertions_test.rs
new file mode 100644
index 0000000..dee073d
--- /dev/null
+++ b/crates/googletest/tests/assertions_test.rs
@@ -0,0 +1,427 @@
+mod verify_pred {
+ use googletest::prelude::*;
+ use indoc::indoc;
+
+ #[test]
+ fn supports_function_call_with_non_debug_types() -> Result<()> {
+ // Non-Debug - cannot be printed.
+ struct Apple;
+ fn f(_a: &Apple, _b: u32, _c: u32) -> bool {
+ false
+ }
+ fn g(_a: u32) -> u32 {
+ 5
+ }
+
+ let a = &Apple;
+ let res = verify_pred!(f(a, g(g(3)), 1 + 2));
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {"
+ f(a, g(g(3)), 1 + 2) was false with
+ a does not implement Debug,
+ g(g(3)) = 5,
+ 1 + 2 = 3,
+ at"
+ })))
+ )
+ }
+
+ #[test]
+ fn supports_trailing_comma() -> Result<()> {
+ verify_that!(verify_pred!(false,), err(anything()))
+ }
+
+ #[test]
+ fn supports_non_function() -> Result<()> {
+ verify_pred!(true)?;
+ verify_that!(verify_pred!(false), err(anything()))
+ }
+
+ #[test]
+ fn does_not_print_literals() -> Result<()> {
+ trait Foo {
+ fn f(&self, _a: u32, _b: i32, _c: u32, _d: &str) -> bool {
+ false
+ }
+ }
+ impl Foo for i32 {}
+
+ let res = verify_pred!(0.f(1, 2_i32.abs(), 1 + 2, "hello"));
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {r#"
+ 0.f(1, 2_i32.abs(), 1 + 2, "hello") was false with
+ 2_i32.abs() = 2,
+ 1 + 2 = 3,
+ at"#
+ })))
+ )
+ }
+
+ #[test]
+ fn supports_chained_field_access_and_method_calls_with_non_debug_types() -> Result<()> {
+ // Non-Debug
+ struct Apple {
+ b: Banana,
+ }
+ #[derive(Debug)]
+ struct Banana;
+ impl Banana {
+ fn c(&self, _c: &Cherry, _d: u32) -> bool {
+ false
+ }
+ }
+ // Non-Debug - cannot be printed.
+ struct Cherry;
+
+ let a = Apple { b: Banana };
+ let c = &Cherry;
+ let d = 3;
+ let res = verify_pred!(a.b.c(c, d));
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {"
+ a.b.c(c, d) was false with
+ a does not implement Debug,
+ a.b = Banana,
+ c does not implement Debug,
+ d = 3,
+ at"
+ })))
+ )
+ }
+
+ #[test]
+ fn evaluates_functions_and_arguments_exactly_once() -> Result<()> {
+ let mut a = 0;
+ let mut foo = |_b: u32| {
+ a += 1;
+ false
+ };
+ let mut b = 0;
+ let mut bar = || {
+ b += 10;
+ b
+ };
+
+ let res = verify_pred!(foo(bar()));
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {"
+ foo(bar()) was false with
+ bar() = 10,
+ at"
+ })))
+ )?;
+
+ verify_that!((a, b), eq((1, 10)))
+ }
+
+ #[test]
+ fn evaluates_methods_and_arguments_exactly_once() -> Result<()> {
+ struct Apple(u32);
+ impl Apple {
+ fn c(&mut self, _b: bool) -> bool {
+ self.0 += 1;
+ false
+ }
+ }
+ let mut a = Apple(0);
+ let mut b = Apple(10);
+
+ let res = verify_pred!(a.c(b.c(false)));
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {"
+ a.c(b.c(false)) was false with
+ a does not implement Debug,
+ b.c(false) = false,
+ at"
+ })))
+ )?;
+
+ verify_that!((a.0, b.0), eq((1, 11)))
+ }
+
+ #[test]
+ fn supports_chained_method_calls() -> Result<()> {
+ #[derive(Debug)]
+ struct Apple;
+ impl Apple {
+ fn b(&self, _b: u32) -> Banana {
+ Banana
+ }
+ }
+ // Non-Debug: not printed on error.
+ struct Banana;
+ impl Banana {
+ fn c(&self, _c0: u32, _c1: Cherry) -> bool {
+ false
+ }
+ }
+ // Non-Debug: not printed on error.
+ #[derive(Copy, Clone)]
+ struct Cherry;
+
+ let a = Apple;
+ let v = 10;
+ let res = verify_pred!(a.b(v).c(11, Cherry));
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {"
+ a.b(v).c(11, Cherry) was false with
+ a = Apple,
+ v = 10,
+ a.b(v) does not implement Debug,
+ Cherry does not implement Debug,
+ at"
+ })))
+ )
+ }
+
+ #[test]
+ fn prints_consumed_values() -> Result<()> {
+ // Non-Debug
+ struct Apple;
+ impl Apple {
+ fn b(self) -> Banana {
+ Banana
+ }
+ }
+ #[derive(Debug)]
+ struct Banana;
+ impl Banana {
+ fn c(self) -> bool {
+ false
+ }
+ }
+
+ let a = Apple;
+ let res = verify_pred!(a.b().c());
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {"
+ a.b().c() was false with
+ a does not implement Debug,
+ a.b() = Banana,
+ at"
+ })))
+ )
+ }
+
+ #[test]
+ fn works_with_realistic_example_with_consumed_intermediate_values() -> Result<()> {
+ let res =
+ verify_pred!(vec![1, 2].into_iter().map(|x| x * 2).collect::<Vec<_>>().is_empty());
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {"
+ vec! [1, 2].into_iter().map(| x | x * 2).collect :: < Vec < _ > >
+ ().is_empty() was false with
+ vec! [1, 2] = [1, 2],
+ vec! [1, 2].into_iter() = IntoIter([1, 2]),
+ | x | x * 2 does not implement Debug,
+ vec! [1, 2].into_iter().map(| x | x * 2) = Map { iter: IntoIter([1, 2]) },
+ vec! [1, 2].into_iter().map(| x | x * 2).collect :: < Vec < _ > > () = [2, 4],
+ at"
+ })))
+ )
+ }
+
+ #[test]
+ fn values_should_be_accessible_after_test() -> Result<()> {
+ // Not `Copy` and should not be consumed by the generated test code.
+ #[derive(Debug)]
+ struct Apple;
+ impl Apple {
+ fn b(&self, _c: &mut u32) -> bool {
+ false
+ }
+ }
+
+ let mut c = 0;
+ let a = Apple;
+ let res = verify_pred!(a.b(&mut c));
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {"
+ a.b(& mut c) was false with
+ a = Apple,
+ & mut c = 0,
+ at"
+ })))
+ )?;
+
+ // `a` and `&mut c` should still be accessible after the test despite not being
+ // `Copy`.
+ let _ = a.b(&mut c);
+
+ Ok(())
+ }
+
+ #[test]
+ fn prints_values_for_mutating_expressions() -> Result<()> {
+ let mut a = 1;
+ let mut b = 2;
+ let mut c = 0;
+ trait Mutator {
+ fn mutate_and_false(&mut self, b: &mut u32) -> bool;
+ }
+ impl Mutator for u32 {
+ fn mutate_and_false(&mut self, b: &mut u32) -> bool {
+ *self += 10;
+ *b += 20;
+ false
+ }
+ }
+
+ // Macro to to avoid the inconsistency in how `;` and `&mut` are printed between
+ // Rust versions when printing out the stringified version of the block.
+ macro_rules! block_a {
+ () => {{
+ c += 10;
+ &mut a
+ }};
+ }
+ macro_rules! block_b {
+ () => {{
+ c += 100;
+ &mut b
+ }};
+ }
+ let res = verify_pred! { block_a!().mutate_and_false(block_b!()) };
+
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {"
+ block_a! ().mutate_and_false(block_b! ()) was false with
+ block_a! () = 1,
+ block_b! () = 2,
+ at"
+ })))
+ )?;
+
+ verify_that!((a, b, c), eq((11, 22, 110)))
+ }
+
+ #[test]
+ fn values_can_be_insulated_with_parens() -> Result<()> {
+ // Not `Copy` and has a consuming method.
+ struct Apple;
+ impl Apple {
+ fn b(self) -> Banana {
+ Banana
+ }
+ }
+ #[derive(Debug)]
+ struct Banana;
+ impl Banana {
+ fn c(&self) -> bool {
+ false
+ }
+ }
+
+ let a = Apple;
+ let res = verify_pred!({ a.b() }.c());
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {"
+ { a.b() }.c() was false with
+ { a.b() } = Banana,
+ at"
+ })))
+ )
+ }
+
+ #[test]
+ fn binary_operator() -> Result<()> {
+ // Add chaining and function calls.
+ fn f(x: u32) -> u32 {
+ x + 1
+ }
+ #[derive(Debug)]
+ struct Apple;
+ impl Apple {
+ fn b(&self, y: u32) -> u32 {
+ y + 10
+ }
+ }
+ let a = Apple;
+ let x = 1;
+ let y = 2;
+ let res = verify_pred!(f(x) - 1 == a.b(y + 1) + f(y));
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {"
+ f(x) - 1 == a.b(y + 1) + f(y) was false with
+ x = 1,
+ f(x) = 2,
+ f(x) - 1 = 1,
+ a = Apple,
+ y + 1 = 3,
+ a.b(y + 1) = 13,
+ y = 2,
+ f(y) = 3,
+ a.b(y + 1) + f(y) = 16,
+ at"
+ })))
+ )
+ }
+
+ #[rustversion::before(1.77)]
+ #[test]
+ fn unary_operator() -> Result<()> {
+ #[derive(Debug)]
+ struct Apple;
+ impl Apple {
+ fn b(&self, _b: u32, _c: i32) -> i32 {
+ 0
+ }
+ }
+
+ let a = Apple;
+ let b = 1;
+ let res = verify_pred!(!a.b(b, -1) == !-2);
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {"
+ ! a.b(b, - 1) ==! - 2 was false with
+ a = Apple,
+ b = 1,
+ a.b(b, - 1) = 0,
+ ! a.b(b, - 1) = -1,
+ ! - 2 = 1,
+ at"
+ })))
+ )
+ }
+
+ #[rustversion::since(1.77)]
+ #[test]
+ fn unary_operator() -> Result<()> {
+ #[derive(Debug)]
+ struct Apple;
+ impl Apple {
+ fn b(&self, _b: u32, _c: i32) -> i32 {
+ 0
+ }
+ }
+
+ let a = Apple;
+ let b = 1;
+ let res = verify_pred!(!a.b(b, -1) == !-2);
+ verify_that!(
+ res,
+ err(displays_as(contains_substring(indoc! {"
+ ! a.b(b, - 1) == ! - 2 was false with
+ a = Apple,
+ b = 1,
+ a.b(b, - 1) = 0,
+ ! a.b(b, - 1) = -1,
+ ! - 2 = 1,
+ at"
+ })))
+ )
+ }
+}
diff --git a/crates/googletest/tests/colorized_diff_test.rs b/crates/googletest/tests/colorized_diff_test.rs
index d056020..2029521 100644
--- a/crates/googletest/tests/colorized_diff_test.rs
+++ b/crates/googletest/tests/colorized_diff_test.rs
@@ -32,7 +32,7 @@
std::env::remove_var("NO_COLOR");
std::env::set_var("FORCE_COLOR", "1");
- let result = verify_that!(build_text(1..50), eq(build_text(1..51)));
+ let result = verify_that!(build_text(1..50), eq(&build_text(1..51)));
verify_that!(
result,
diff --git a/crates/googletest/tests/composition_test.rs b/crates/googletest/tests/composition_test.rs
index 7ae146f..e5f2cd7 100644
--- a/crates/googletest/tests/composition_test.rs
+++ b/crates/googletest/tests/composition_test.rs
@@ -17,14 +17,14 @@
#[test]
fn all_matcher_works_as_inner_matcher() -> Result<()> {
let value = vec![1];
- verify_that!(value, contains_each![all!(gt(0), lt(2))])
+ verify_that!(value, contains_each![all!(gt(&0), lt(&2))])
}
#[test]
fn matches_pattern_works_as_inner_matcher() -> Result<()> {
#[derive(Debug)]
struct AStruct(i32);
- verify_that!(vec![AStruct(123)], contains_each![matches_pattern!(AStruct(eq(123)))])
+ verify_that!(vec![AStruct(123)], contains_each![matches_pattern!(&AStruct(eq(123)))])
}
#[test]
@@ -38,7 +38,7 @@
}
verify_that!(
vec![AStruct(123)],
- contains_each![matches_pattern!(AStruct {
+ contains_each![matches_pattern!(&AStruct {
get_value(): eq(123)
})]
)
@@ -48,24 +48,139 @@
fn contains_each_works_as_inner_matcher() -> Result<()> {
#[derive(Debug)]
struct AStruct(Vec<i32>);
- verify_that!(AStruct(vec![123]), matches_pattern!(AStruct(contains_each![eq(123)])))
+ verify_that!(AStruct(vec![123]), matches_pattern!(&AStruct(ref contains_each![eq(&123)])))
}
#[test]
fn pointwise_works_as_inner_matcher() -> Result<()> {
#[derive(Debug)]
struct AStruct(Vec<i32>);
- verify_that!(AStruct(vec![123]), matches_pattern!(AStruct(pointwise!(eq, [123]))))
+ verify_that!(AStruct(vec![123]), matches_pattern!(&AStruct(ref pointwise!(eq, [&123]))))
}
#[test]
fn elements_are_works_as_inner_matcher() -> Result<()> {
#[derive(Debug)]
struct AStruct(Vec<i32>);
- verify_that!(AStruct(vec![123]), matches_pattern!(AStruct(elements_are![eq(123)])))
+ verify_that!(AStruct(vec![123]), matches_pattern!(&AStruct(ref elements_are![eq(&123)])))
}
#[test]
fn tuple_works_as_inner_matcher() -> Result<()> {
- verify_that!(vec![(123,)], elements_are![(eq(123),)])
+ verify_that!(vec![(123,)], elements_are![(eq(&123),)])
+}
+
+#[test]
+fn matches_struct_with_method_returning_option_of_non_copy_value() -> Result<()> {
+ #[derive(Debug)]
+ struct AnInnerStruct;
+
+ #[derive(Debug)]
+ struct AStruct;
+
+ impl AStruct {
+ fn get_value(&self) -> Option<AnInnerStruct> {
+ Some(AnInnerStruct)
+ }
+ }
+
+ verify_that!(
+ AStruct,
+ matches_pattern!(&AStruct {
+ get_value(): ref some(matches_pattern!(&AnInnerStruct))
+ })
+ )
+}
+
+#[test]
+fn matches_struct_with_method_returning_option_of_non_copy_enum() -> Result<()> {
+ #[derive(Debug)]
+ enum AnInnerStruct {
+ ThisCase,
+ #[allow(unused)]
+ ThatCase,
+ }
+ #[derive(Debug)]
+ struct AStruct;
+ impl AStruct {
+ fn get_value(&self) -> Option<AnInnerStruct> {
+ Some(AnInnerStruct::ThisCase)
+ }
+ }
+
+ verify_that!(
+ AStruct,
+ matches_pattern!(&AStruct {
+ get_value(): ref some(matches_pattern!(&AnInnerStruct::ThisCase))
+ })
+ )
+}
+
+#[test]
+fn matches_struct_with_method_returning_option_ref_binding_mode() -> Result<()> {
+ #[derive(Debug)]
+ struct AnInnerStruct;
+ #[derive(Debug)]
+ struct AStruct;
+ impl AStruct {
+ fn get_value(&self) -> Option<AnInnerStruct> {
+ Some(AnInnerStruct)
+ }
+ }
+
+ verify_that!(
+ AStruct,
+ matches_pattern!(AStruct {
+ get_value(): some(matches_pattern!(AnInnerStruct))
+ })
+ )
+}
+
+#[test]
+fn matches_struct_with_method_returning_option_enum_ref_binding_mode() -> Result<()> {
+ #[derive(Debug)]
+ enum AnInnerStruct {
+ ThisCase,
+ #[allow(unused)]
+ ThatCase,
+ }
+ #[derive(Debug)]
+ struct AStruct;
+ impl AStruct {
+ fn get_value(&self) -> Option<AnInnerStruct> {
+ Some(AnInnerStruct::ThisCase)
+ }
+ }
+
+ verify_that!(
+ AStruct,
+ matches_pattern!(AStruct {
+ get_value(): some(matches_pattern!(AnInnerStruct::ThisCase))
+ })
+ )
+}
+
+#[test]
+fn matches_struct_with_property_against_predicate() -> Result<()> {
+ #[derive(Debug)]
+ enum AnInnerStruct {
+ ThisCase,
+ #[allow(unused)]
+ ThatCase,
+ }
+
+ #[derive(Debug)]
+ struct AStruct;
+ impl AStruct {
+ fn get_value(&self) -> AnInnerStruct {
+ AnInnerStruct::ThisCase
+ }
+ }
+
+ verify_that!(
+ AStruct,
+ matches_pattern!(AStruct {
+ get_value(): predicate(|_: &_| true)
+ })
+ )
}
diff --git a/crates/googletest/tests/elements_are_matcher_test.rs b/crates/googletest/tests/elements_are_matcher_test.rs
index 4de2314..3aafbc7 100644
--- a/crates/googletest/tests/elements_are_matcher_test.rs
+++ b/crates/googletest/tests/elements_are_matcher_test.rs
@@ -12,21 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use googletest::matcher::Matcher;
use googletest::prelude::*;
use indoc::indoc;
#[test]
fn elements_are_matches_vector() -> Result<()> {
let value = vec![1, 2, 3];
- verify_that!(value, elements_are![eq(1), eq(2), eq(3)])
+ verify_that!(value, elements_are![eq(&1), eq(&2), eq(&3)])
}
#[test]
fn elements_are_matches_slice() -> Result<()> {
let value = vec![1, 2, 3];
let slice = value.as_slice();
- verify_that!(*slice, elements_are![eq(1), eq(2), eq(3)])
+ verify_that!(slice, elements_are![eq(&1), eq(&2), eq(&3)])
}
#[test]
@@ -37,13 +36,13 @@
#[test]
fn elements_are_supports_trailing_comma() -> Result<()> {
let value = vec![1, 2, 3];
- verify_that!(value, elements_are![eq(1), eq(2), eq(3),])
+ verify_that!(value, elements_are![eq(&1), eq(&2), eq(&3),])
}
#[test]
fn elements_are_returns_no_match_when_expected_and_actual_sizes_differ() -> Result<()> {
let value = vec![1, 2];
- verify_that!(value, not(elements_are![eq(1), eq(2), eq(3)]))
+ verify_that!(value, not(elements_are![eq(&1), eq(&2), eq(&3)]))
}
#[test]
@@ -51,12 +50,32 @@
#[derive(Debug, PartialEq)]
struct AStruct(i32);
let expected_value = AStruct(123);
- verify_that!(vec![AStruct(123)], elements_are![eq_deref_of(&expected_value)])
+ verify_that!(vec![AStruct(123)], elements_are![eq(&expected_value)])
+}
+
+#[test]
+fn elements_are_matches_iterator_returning_by_value() -> Result<()> {
+ #[derive(Debug, Copy, Clone)]
+ struct Countdown(i32);
+ impl Iterator for Countdown {
+ type Item = i32;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.0 {
+ 0 => None,
+ x => {
+ self.0 -= 1;
+ Some(x)
+ }
+ }
+ }
+ }
+ verify_that!(Countdown(3), elements_are![eq(3), eq(2), eq(1)])
}
#[test]
fn elements_are_produces_correct_failure_message() -> Result<()> {
- let result = verify_that!(vec![1, 4, 3], elements_are![eq(1), eq(2), eq(3)]);
+ let result = verify_that!(vec![1, 4, 3], elements_are![eq(&1), eq(&2), eq(&3)]);
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
@@ -76,7 +95,7 @@
fn elements_are_produces_correct_failure_message_nested() -> Result<()> {
let result = verify_that!(
vec![vec![0, 1], vec![1, 2]],
- elements_are![elements_are![eq(1), eq(2)], elements_are![eq(2), eq(3)]]
+ elements_are![elements_are![eq(&1), eq(&2)], elements_are![eq(&2), eq(&3)]]
);
verify_that!(
result,
@@ -103,14 +122,12 @@
#[test]
fn elements_are_explain_match_wrong_size() -> Result<()> {
- verify_that!(
- elements_are![eq(1)].explain_match(&vec![1, 2]),
- displays_as(eq("whose size is 2"))
- )
+ let matcher = elements_are![eq(&1)];
+ verify_that!(matcher.explain_match(&vec![1, 2]), displays_as(eq("whose size is 2")))
}
-fn create_matcher() -> impl Matcher<ActualT = Vec<i32>> {
- elements_are![eq(1)]
+fn create_matcher<'a>() -> impl Matcher<&'a Vec<i32>> {
+ elements_are![eq(&1)]
}
#[test]
@@ -120,5 +137,10 @@
#[test]
fn elements_are_implicitly_called() -> Result<()> {
- verify_that!(vec![1, 2, 3], [eq(1), eq(2), eq(3)])
+ verify_that!(vec![1, 2, 3], [eq(&1), eq(&2), eq(&3)])
+}
+
+#[test]
+fn elements_are_with_auto_eq() -> Result<()> {
+ verify_that!(vec![1, 2, 3], [&1, &2, lt(&43)])
}
diff --git a/crates/googletest/tests/field_matcher_test.rs b/crates/googletest/tests/field_matcher_test.rs
index f585c21..c239b47 100644
--- a/crates/googletest/tests/field_matcher_test.rs
+++ b/crates/googletest/tests/field_matcher_test.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use googletest::matcher::{Matcher, MatcherResult};
+use googletest::matcher::MatcherResult;
use googletest::prelude::*;
#[derive(Debug)]
@@ -22,7 +22,7 @@
#[test]
fn field_matches_integer_field() -> Result<()> {
- verify_that!(IntField { int: 32 }, field!(IntField.int, eq(32)))
+ verify_that!(IntField { int: 32 }, field!(&IntField.int, eq(32)))
}
#[derive(Debug)]
@@ -32,12 +32,15 @@
#[test]
fn field_matches_string_field() -> Result<()> {
- verify_that!(StringField { strink: "yes".to_string() }, field!(StringField.strink, eq("yes")))
+ verify_that!(
+ StringField { strink: "yes".to_string() },
+ field!(&StringField.strink, ref eq("yes"))
+ )
}
#[test]
fn field_error_message_shows_field_name_and_inner_matcher() -> Result<()> {
- let matcher = field!(IntField.int, eq(31));
+ let matcher = field!(&IntField.int, eq(31));
verify_that!(
matcher.describe(MatcherResult::Match),
@@ -54,15 +57,15 @@
#[test]
fn struct_in_other_module_matches() -> Result<()> {
- verify_that!(sub::SubStruct { field: 32 }, field!(sub::SubStruct.field, eq(32)))
+ verify_that!(sub::SubStruct { field: 32 }, field!(&sub::SubStruct.field, eq(32)))
}
#[derive(Debug)]
-struct Tuple(i32, String);
+struct Tuple(i32, #[allow(unused)] String);
#[test]
fn tuple_matches_with_index() -> Result<()> {
- verify_that!(Tuple(32, "yes".to_string()), field!(Tuple.0, eq(32)))
+ verify_that!(Tuple(32, "yes".to_string()), field!(&Tuple.0, eq(32)))
}
#[test]
@@ -73,7 +76,7 @@
}
let value = AnEnum::AValue(123);
- verify_that!(value, field!(AnEnum::AValue.0, eq(123)))
+ verify_that!(value, field!(&AnEnum::AValue.0, eq(123)))
}
#[test]
@@ -84,7 +87,7 @@
}
let value = AStruct { a: vec![1] };
- let result = verify_that!(value, field!(AStruct.a, container_eq([])));
+ let result = verify_that!(value, field!(&AStruct.a, ref container_eq([])));
verify_that!(
result,
@@ -104,7 +107,7 @@
}
let value = AnEnum::AnotherValue;
- verify_that!(value, not(field!(AnEnum::AValue.0, eq(123))))
+ verify_that!(&value, not(field!(&AnEnum::AValue.0, eq(123))))
}
#[test]
@@ -119,7 +122,7 @@
}
let value = AnEnum::AnotherValue;
- let result = verify_that!(value, field!(AnEnum::AValue.a, eq(123)));
+ let result = verify_that!(value, field!(&AnEnum::AValue.a, eq(123)));
verify_that!(
result,
@@ -133,11 +136,12 @@
enum AnEnum {
#[allow(dead_code)] // This variant is intentionally unused.
AValue(u32),
+ #[allow(unused)]
AnotherValue(u32),
}
let value = AnEnum::AnotherValue(123);
- let result = verify_that!(value, field!(AnEnum::AValue.0, eq(123)));
+ let result = verify_that!(value, field!(&AnEnum::AValue.0, eq(123)));
verify_that!(
result,
@@ -158,7 +162,7 @@
}
let value = AnEnum::AnotherValue { a: 123 };
- let result = verify_that!(value, field!(AnEnum::AValue.0, eq(123)));
+ let result = verify_that!(value, field!(&AnEnum::AValue.0, eq(123)));
verify_that!(
result,
@@ -174,5 +178,87 @@
}
let value = AnEnum::AValue { a_field: 123 };
- verify_that!(value, field!(AnEnum::AValue.a_field, eq(123)))
+ verify_that!(value, field!(&AnEnum::AValue.a_field, eq(123)))
+}
+
+#[test]
+fn matches_struct_copy_to_copy() -> Result<()> {
+ #[derive(Debug, Clone, Copy)]
+ struct Strukt {
+ a_field: i32,
+ }
+
+ verify_that!(Strukt { a_field: 32 }, field!(Strukt.a_field, eq(32)))
+}
+
+#[test]
+fn matches_struct_ref_to_copy() -> Result<()> {
+ #[derive(Debug)]
+ struct Strukt {
+ a_field: i32,
+ }
+
+ verify_that!(Strukt { a_field: 32 }, field!(&Strukt.a_field, eq(32)))
+}
+
+#[test]
+fn matches_struct_ref_to_ref() -> Result<()> {
+ #[derive(Debug)]
+ struct Strukt {
+ a_field: String,
+ }
+
+ verify_that!(Strukt { a_field: "32".into() }, field!(&Strukt.a_field, ref eq("32")))
+}
+
+#[test]
+fn matches_struct_copy_to_ref() -> Result<()> {
+ // It is not possible to have a copy struct with non-copy field. Hence, this
+ // test case is not necessary.
+ Ok(())
+}
+
+#[test]
+fn matches_struct_ref_to_ref_binding_mode() -> Result<()> {
+ #[derive(Debug)]
+ struct Strukt {
+ a_field: String,
+ }
+
+ verify_that!(Strukt { a_field: "32".into() }, field!(Strukt.a_field, eq("32")))
+}
+
+#[test]
+fn matches_struct_with_auto_eq() -> Result<()> {
+ #[derive(Debug)]
+ struct Strukt {
+ a_field: String,
+ }
+
+ verify_that!(Strukt { a_field: "32".into() }, field!(Strukt.a_field, "32"))
+}
+
+#[test]
+fn matches_enum_with_auto_eq() -> Result<()> {
+ #[derive(Debug)]
+ enum Enum {
+ Str(String),
+ #[allow(unused)]
+ Int(i32),
+ }
+
+ verify_that!(Enum::Str("32".into()), field!(Enum::Str.0, "32"))
+}
+
+#[test]
+fn matches_enum_with_auto_eq_with_wrapper() -> Result<()> {
+ #[derive(Debug)]
+ struct Wrapper<I> {
+ wrapped: I,
+ }
+
+ verify_that!(
+ Wrapper { wrapped: Wrapper { wrapped: 23 } },
+ field!(Wrapper.wrapped, field!(Wrapper.wrapped, &23))
+ )
}
diff --git a/crates/googletest/tests/fmt_test.rs b/crates/googletest/tests/fmt_test.rs
new file mode 100644
index 0000000..63e2dd7
--- /dev/null
+++ b/crates/googletest/tests/fmt_test.rs
@@ -0,0 +1,58 @@
+// Copyright 2024 Google LLC
+//
+// 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.
+
+mod write_expr_value {
+ use googletest::prelude::*;
+
+ // Converts the formatting call to a `String` for testing.
+ macro_rules! write_expr_value {
+ ($expr_str:expr, $expr: expr $(,)?) => {{
+ let mut s = String::new();
+ ::googletest::fmt::internal::__googletest__write_expr_value!(s, $expr_str, $expr);
+ s
+ }};
+ }
+
+ #[test]
+ fn test_with_debug_value_references() -> Result<()> {
+ #[derive(Debug)]
+ struct Foo;
+ let mut val = Foo;
+
+ verify_that!(write_expr_value!("val", val), eq("\n val = Foo,"))?;
+ verify_that!(write_expr_value!("val", &val), eq("\n val = Foo,"))?;
+ verify_that!(write_expr_value!("val", &&val), eq("\n val = Foo,"))?;
+ verify_that!(write_expr_value!("val", &mut val), eq("\n val = Foo,"))?;
+ verify_that!(write_expr_value!("val", &mut &mut val), eq("\n val = Foo,"))?;
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_with_non_debug_value_references() -> Result<()> {
+ struct Foo;
+ let mut val = Foo;
+
+ verify_that!(write_expr_value!("val", val), eq("\n val does not implement Debug,"))?;
+ verify_that!(write_expr_value!("val", &val), eq("\n val does not implement Debug,"))?;
+ verify_that!(write_expr_value!("val", &&val), eq("\n val does not implement Debug,"))?;
+ verify_that!(write_expr_value!("val", &mut val), eq("\n val does not implement Debug,"))?;
+ verify_that!(
+ write_expr_value!("val", &mut &mut val),
+ eq("\n val does not implement Debug,")
+ )?;
+
+ Ok(())
+ }
+}
diff --git a/crates/googletest/tests/lib.rs b/crates/googletest/tests/lib.rs
index fb243ca..f5d8f75 100644
--- a/crates/googletest/tests/lib.rs
+++ b/crates/googletest/tests/lib.rs
@@ -14,6 +14,7 @@
mod all_matcher_test;
mod any_matcher_test;
+mod assertions_test;
mod colorized_diff_test;
mod composition_test;
mod elements_are_matcher_test;
@@ -21,7 +22,6 @@
mod matches_pattern_test;
mod pointwise_matcher_test;
mod property_matcher_test;
-#[cfg(feature = "proptest")]
mod proptest_integration_test;
mod tuple_matcher_test;
mod unordered_elements_are_matcher_test;
diff --git a/crates/googletest/tests/matches_pattern_test.rs b/crates/googletest/tests/matches_pattern_test.rs
index 3298e36..d1b97e7 100644
--- a/crates/googletest/tests/matches_pattern_test.rs
+++ b/crates/googletest/tests/matches_pattern_test.rs
@@ -23,7 +23,7 @@
}
let actual = AStruct { a_field: 123 };
- verify_that!(actual, matches_pattern!(AStruct { a_field: eq(123) }))
+ verify_that!(actual, matches_pattern!(&AStruct { a_field: eq(123) }))
}
#[test]
@@ -35,7 +35,7 @@
}
let actual = AStruct { a_field: 123, another_field: 234 };
- verify_that!(actual, matches_pattern!(AStruct { a_field: eq(123), another_field: eq(234) }))
+ verify_that!(actual, matches_pattern!(&AStruct { a_field: eq(123), another_field: eq(234) }))
}
#[test]
@@ -47,7 +47,7 @@
}
let actual = AStruct { a_field: 123 };
- verify_that!(actual, matches_pattern!(AStruct {
+ verify_that!(actual, matches_pattern!(&AStruct {
a_field: eq(123), // Block reformatting
}))
}
@@ -63,7 +63,7 @@
verify_that!(
actual,
- matches_pattern!(AStruct {
+ matches_pattern!(&AStruct {
a_field: eq(123),
another_field: eq(234), // Block reformatting
})
@@ -82,7 +82,7 @@
verify_that!(
actual,
- matches_pattern!(AStruct {
+ matches_pattern!(&AStruct {
a_field: eq(123),
another_field: eq(234),
a_third_field: eq(345),
@@ -104,29 +104,69 @@
verify_that!(
actual,
- matches_pattern!(AStruct { a_nested_struct: pat!(ANestedStruct { a_field: eq(123) }) })
+ matches_pattern!(&AStruct { a_nested_struct: ref pat!(&ANestedStruct { a_field: eq(123) }) })
)
}
#[test]
+fn matches_struct_containing_nested_struct_with_field_with_binding_mode() -> Result<()> {
+ #[derive(Debug)]
+ struct AStruct {
+ a_nested_struct: ANestedStruct,
+ }
+ #[derive(Debug)]
+ struct ANestedStruct {
+ a_field: u32,
+ }
+ let actual = AStruct { a_nested_struct: ANestedStruct { a_field: 123 } };
+
+ verify_that!(
+ actual,
+ matches_pattern!(AStruct { a_nested_struct: pat!(ANestedStruct { a_field: eq(&123) }) })
+ )
+}
+
+#[test]
+fn matches_struct_containing_non_copy_field_binding_mode() -> Result<()> {
+ #[derive(Debug)]
+ struct AStruct {
+ a_string: String,
+ }
+ let actual = AStruct { a_string: "123".into() };
+
+ verify_that!(actual, matches_pattern!(AStruct { a_string: eq("123") }))
+}
+
+#[test]
fn has_correct_assertion_failure_message_for_single_field() -> Result<()> {
#[derive(Debug)]
struct AStruct {
a_field: u32,
}
let actual = AStruct { a_field: 123 };
- let result = verify_that!(actual, matches_pattern!(AStruct { a_field: eq(234) }));
+ let result = verify_that!(actual, matches_pattern!(&AStruct { a_field: eq(234) }));
- verify_that!(
- result,
- err(displays_as(contains_substring(indoc! {"
- Value of: actual
- Expected: is AStruct which has field `a_field`, which is equal to 234
- Actual: AStruct { a_field: 123 },
- which has field `a_field`, which isn't equal to 234
- "
- })))
- )
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = indoc!(
+ "
+ Value of: actual
+ Expected: is & AStruct which has field `a_field`, which is equal to 234
+ Actual: AStruct { a_field: 123 },
+ which has field `a_field`, which isn't equal to 234
+ "
+ );
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = indoc!(
+ "
+ Value of: actual
+ Expected: is &AStruct which has field `a_field`, which is equal to 234
+ Actual: AStruct { a_field: 123 },
+ which has field `a_field`, which isn't equal to 234
+ "
+ );
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
#[test]
@@ -139,21 +179,34 @@
let actual = AStruct { a_field: 123, another_field: 234 };
let result = verify_that!(
actual,
- matches_pattern!(AStruct { a_field: eq(234), another_field: eq(123) })
+ matches_pattern!(&AStruct { a_field: eq(234), another_field: eq(123) })
);
- verify_that!(
- result,
- err(displays_as(contains_substring(indoc!(
- "
- Value of: actual
- Expected: is AStruct which has all the following properties:
- * has field `a_field`, which is equal to 234
- * has field `another_field`, which is equal to 123
- Actual: AStruct { a_field: 123, another_field: 234 },
- * which has field `a_field`, which isn't equal to 234
- * which has field `another_field`, which isn't equal to 123"
- ))))
- )
+
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = indoc!(
+ "
+ Value of: actual
+ Expected: is & AStruct which has all the following properties:
+ * has field `a_field`, which is equal to 234
+ * has field `another_field`, which is equal to 123
+ Actual: AStruct { a_field: 123, another_field: 234 },
+ * which has field `a_field`, which isn't equal to 234
+ * which has field `another_field`, which isn't equal to 123"
+ );
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = indoc!(
+ "
+ Value of: actual
+ Expected: is &AStruct which has all the following properties:
+ * has field `a_field`, which is equal to 234
+ * has field `another_field`, which is equal to 123
+ Actual: AStruct { a_field: 123, another_field: 234 },
+ * which has field `a_field`, which isn't equal to 234
+ * which has field `another_field`, which isn't equal to 123"
+ );
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
#[test]
@@ -171,33 +224,47 @@
let actual = AStruct { a_field: 123, another_field: 234 };
let result = verify_that!(
actual,
- matches_pattern!(AStruct { get_field(): eq(234), another_field: eq(123) })
+ matches_pattern!(&AStruct { get_field(): eq(234), another_field: eq(123) })
);
- verify_that!(
- result,
- err(displays_as(contains_substring(indoc!(
- "
- Value of: actual
- Expected: is AStruct which has all the following properties:
- * has property `get_field ()`, which is equal to 234
- * has field `another_field`, which is equal to 123
- Actual: AStruct { a_field: 123, another_field: 234 },
- * whose property `get_field ()` is `123`, which isn't equal to 234
- * which has field `another_field`, which isn't equal to 123"
- ))))
- )
+
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = indoc!(
+ "
+ Value of: actual
+ Expected: is & AStruct which has all the following properties:
+ * has property `get_field ()`, which is equal to 234
+ * has field `another_field`, which is equal to 123
+ Actual: AStruct { a_field: 123, another_field: 234 },
+ * whose property `get_field ()` is `123`, which isn't equal to 234
+ * which has field `another_field`, which isn't equal to 123"
+ );
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = indoc!(
+ "
+ Value of: actual
+ Expected: is &AStruct which has all the following properties:
+ * has property `get_field ()`, which is equal to 234
+ * has field `another_field`, which is equal to 123
+ Actual: AStruct { a_field: 123, another_field: 234 },
+ * whose property `get_field ()` is `123`, which isn't equal to 234
+ * which has field `another_field`, which isn't equal to 123"
+ );
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
#[test]
fn has_meaningful_assertion_failure_message_when_wrong_enum_variant_is_used() -> Result<()> {
#[derive(Debug)]
enum AnEnum {
+ #[allow(unused)]
A(u32),
#[allow(unused)]
B(u32),
}
let actual = AnEnum::A(123);
- let result = verify_that!(actual, matches_pattern!(AnEnum::B(eq(123))));
+ let result = verify_that!(actual, matches_pattern!(&AnEnum::B(eq(123))));
verify_that!(
result,
@@ -219,7 +286,7 @@
}
let actual = a_module::AStruct { a_field: 123 };
- verify_that!(actual, matches_pattern!(a_module::AStruct { a_field: eq(123) }))
+ verify_that!(actual, matches_pattern!(&a_module::AStruct { a_field: eq(123) }))
}
#[test]
@@ -228,7 +295,7 @@
struct AStruct(u32);
let actual = AStruct(123);
- verify_that!(actual, matches_pattern!(AStruct(eq(123))))
+ verify_that!(actual, matches_pattern!(&AStruct(eq(123))))
}
#[test]
@@ -237,7 +304,7 @@
struct AStruct(u32, u32);
let actual = AStruct(123, 234);
- verify_that!(actual, matches_pattern!(AStruct(eq(123), eq(234))))
+ verify_that!(actual, matches_pattern!(&AStruct(eq(123), eq(234))))
}
#[test]
@@ -246,7 +313,7 @@
struct AStruct(u32, u32, u32);
let actual = AStruct(123, 234, 345);
- verify_that!(actual, matches_pattern!(AStruct(eq(123), eq(234), eq(345))))
+ verify_that!(actual, matches_pattern!(&AStruct(eq(123), eq(234), eq(345))))
}
#[test]
@@ -255,7 +322,7 @@
struct AStruct(u32, u32, u32, u32);
let actual = AStruct(123, 234, 345, 456);
- verify_that!(actual, matches_pattern!(AStruct(eq(123), eq(234), eq(345), eq(456))))
+ verify_that!(actual, matches_pattern!(&AStruct(eq(123), eq(234), eq(345), eq(456))))
}
#[test]
@@ -264,7 +331,7 @@
struct AStruct(u32, u32, u32, u32, u32);
let actual = AStruct(123, 234, 345, 456, 567);
- verify_that!(actual, matches_pattern!(AStruct(eq(123), eq(234), eq(345), eq(456), eq(567))))
+ verify_that!(actual, matches_pattern!(&AStruct(eq(123), eq(234), eq(345), eq(456), eq(567))))
}
#[test]
@@ -275,7 +342,7 @@
verify_that!(
actual,
- matches_pattern!(AStruct(eq(123), eq(234), eq(345), eq(456), eq(567), eq(678)))
+ matches_pattern!(&AStruct(eq(123), eq(234), eq(345), eq(456), eq(567), eq(678)))
)
}
@@ -287,7 +354,7 @@
verify_that!(
actual,
- matches_pattern!(AStruct(eq(123), eq(234), eq(345), eq(456), eq(567), eq(678), eq(789)))
+ matches_pattern!(&AStruct(eq(123), eq(234), eq(345), eq(456), eq(567), eq(678), eq(789)))
)
}
@@ -299,7 +366,7 @@
verify_that!(
actual,
- matches_pattern!(AStruct(
+ matches_pattern!(&AStruct(
eq(123),
eq(234),
eq(345),
@@ -320,7 +387,7 @@
verify_that!(
actual,
- matches_pattern!(AStruct(
+ matches_pattern!(&AStruct(
eq(123),
eq(234),
eq(345),
@@ -342,7 +409,7 @@
verify_that!(
actual,
- matches_pattern!(AStruct(
+ matches_pattern!(&AStruct(
eq(123),
eq(234),
eq(345),
@@ -358,6 +425,29 @@
}
#[test]
+fn matches_tuple_struct_containing_ten_fields_by_ref() -> Result<()> {
+ #[derive(Debug)]
+ struct AStruct(u32, u32, u32, u32, u32, u32, u32, u32, u32, u32);
+ let actual = AStruct(123, 234, 345, 456, 567, 678, 789, 890, 901, 12);
+
+ verify_that!(
+ actual,
+ matches_pattern!(&AStruct(
+ ref eq(&123),
+ ref eq(&234),
+ ref eq(&345),
+ ref eq(&456),
+ ref eq(&567),
+ ref eq(&678),
+ ref eq(&789),
+ ref eq(&890),
+ ref eq(&901),
+ ref eq(&12)
+ ))
+ )
+}
+
+#[test]
fn matches_tuple_struct_with_trailing_comma() -> Result<()> {
#[derive(Debug)]
struct AStruct(u32);
@@ -365,7 +455,7 @@
verify_that!(
actual,
- matches_pattern!(AStruct(
+ matches_pattern!(&AStruct(
eq(123), // Keep the trailing comma, block reformatting
))
)
@@ -379,7 +469,7 @@
verify_that!(
actual,
- matches_pattern!(AStruct(
+ matches_pattern!(&AStruct(
eq(123),
eq(234), // Keep the trailing comma, block reformatting
))
@@ -394,14 +484,30 @@
}
let actual = AnEnum::A;
+ verify_that!(actual, matches_pattern!(&AnEnum::A))
+}
+
+#[test]
+fn matches_enum_without_field_ref_binding_mode() -> Result<()> {
+ #[derive(Debug)]
+ enum AnEnum {
+ A,
+ }
+ let actual = AnEnum::A;
+
verify_that!(actual, matches_pattern!(AnEnum::A))
}
-#[rustversion::before(1.76)]
-const ANENUM_A_REPR: &str = "AnEnum :: A";
+#[test]
+fn matches_enum_without_field_copy() -> Result<()> {
+ #[derive(Debug, Clone, Copy)]
+ enum AnEnum {
+ A,
+ }
+ let actual = AnEnum::A;
-#[rustversion::since(1.76)]
-const ANENUM_A_REPR: &str = "AnEnum::A";
+ verify_that!(actual, matches_pattern!(AnEnum::A))
+}
#[test]
fn generates_correct_failure_output_when_enum_variant_without_field_is_not_matched() -> Result<()> {
@@ -413,11 +519,16 @@
}
let actual = AnEnum::B;
- let result = verify_that!(actual, matches_pattern!(AnEnum::A));
+ let result = verify_that!(actual, matches_pattern!(&AnEnum::A));
- verify_that!(result, err(displays_as(contains_substring(format!("is not {ANENUM_A_REPR}")))))
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = "is not & AnEnum :: A";
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = "is not &AnEnum::A";
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
-
#[test]
fn generates_correct_failure_output_when_enum_variant_without_field_is_matched() -> Result<()> {
#[derive(Debug)]
@@ -426,11 +537,16 @@
}
let actual = AnEnum::A;
- let result = verify_that!(actual, not(matches_pattern!(AnEnum::A)));
+ let result = verify_that!(actual, not(matches_pattern!(&AnEnum::A)));
- verify_that!(result, err(displays_as(contains_substring(format!("is {ANENUM_A_REPR}")))))
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = "is & AnEnum :: A";
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = "is &AnEnum::A";
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
-
#[test]
fn matches_enum_with_field() -> Result<()> {
#[derive(Debug)]
@@ -439,9 +555,8 @@
}
let actual = AnEnum::A(123);
- verify_that!(actual, matches_pattern!(AnEnum::A(eq(123))))
+ verify_that!(actual, matches_pattern!(&AnEnum::A(eq(123))))
}
-
#[test]
fn does_not_match_wrong_enum_value() -> Result<()> {
#[derive(Debug)]
@@ -452,9 +567,8 @@
}
let actual = AnEnum::B;
- verify_that!(actual, not(matches_pattern!(AnEnum::A(eq(123)))))
+ verify_that!(actual, not(matches_pattern!(&AnEnum::A(eq(123)))))
}
-
#[test]
fn includes_enum_variant_in_description_with_field() -> Result<()> {
#[derive(Debug)]
@@ -463,16 +577,16 @@
}
let actual = AnEnum::A(123);
- let result = verify_that!(actual, matches_pattern!(AnEnum::A(eq(234))));
+ let result = verify_that!(actual, matches_pattern!(&AnEnum::A(eq(234))));
- verify_that!(
- result,
- err(displays_as(contains_substring(format!(
- "Expected: is {ANENUM_A_REPR} which has field `0`"
- ))))
- )
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = "Expected: is & AnEnum :: A which has field `0`";
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = "Expected: is &AnEnum::A which has field `0`";
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
-
#[test]
fn includes_enum_variant_in_negative_description_with_field() -> Result<()> {
#[derive(Debug)]
@@ -481,16 +595,16 @@
}
let actual = AnEnum::A(123);
- let result = verify_that!(actual, not(matches_pattern!(AnEnum::A(eq(123)))));
+ let result = verify_that!(actual, not(matches_pattern!(&AnEnum::A(eq(123)))));
- verify_that!(
- result,
- err(displays_as(contains_substring(format!(
- "Expected: is not {ANENUM_A_REPR} which has field `0`, which is equal to"
- ))))
- )
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = "Expected: is not & AnEnum :: A which has field `0`, which is equal to";
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = "Expected: is not &AnEnum::A which has field `0`, which is equal to";
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
-
#[test]
fn includes_enum_variant_in_description_with_two_fields() -> Result<()> {
#[derive(Debug)]
@@ -499,16 +613,16 @@
}
let actual = AnEnum::A(123, 234);
- let result = verify_that!(actual, matches_pattern!(AnEnum::A(eq(234), eq(234))));
+ let result = verify_that!(actual, matches_pattern!(&AnEnum::A(eq(234), eq(234))));
- verify_that!(
- result,
- err(displays_as(contains_substring(format!(
- "Expected: is {ANENUM_A_REPR} which has all the following properties"
- ))))
- )
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = "Expected: is & AnEnum :: A which has all the following properties";
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = "Expected: is &AnEnum::A which has all the following properties";
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
-
#[test]
fn includes_enum_variant_in_description_with_three_fields() -> Result<()> {
#[derive(Debug)]
@@ -517,16 +631,16 @@
}
let actual = AnEnum::A(123, 234, 345);
- let result = verify_that!(actual, matches_pattern!(AnEnum::A(eq(234), eq(234), eq(345))));
+ let result = verify_that!(actual, matches_pattern!(&AnEnum::A(eq(234), eq(234), eq(345))));
- verify_that!(
- result,
- err(displays_as(contains_substring(format!(
- "Expected: is {ANENUM_A_REPR} which has all the following properties"
- ))))
- )
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = "Expected: is & AnEnum :: A which has all the following properties";
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = "Expected: is &AnEnum::A which has all the following properties";
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
-
#[test]
fn includes_enum_variant_in_description_with_named_field() -> Result<()> {
#[derive(Debug)]
@@ -535,16 +649,16 @@
}
let actual = AnEnum::A { field: 123 };
- let result = verify_that!(actual, matches_pattern!(AnEnum::A { field: eq(234) }));
+ let result = verify_that!(actual, matches_pattern!(&AnEnum::A { field: eq(234) }));
- verify_that!(
- result,
- err(displays_as(contains_substring(format!(
- "Expected: is {ANENUM_A_REPR} which has field `field`"
- ))))
- )
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = "Expected: is & AnEnum :: A which has field `field`";
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = "Expected: is &AnEnum::A which has field `field`";
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
-
#[test]
fn includes_enum_variant_in_description_with_two_named_fields() -> Result<()> {
#[derive(Debug)]
@@ -555,17 +669,17 @@
let result = verify_that!(
actual,
- matches_pattern!(AnEnum::A { field: eq(234), another_field: eq(234) })
+ matches_pattern!(&AnEnum::A { field: eq(234), another_field: eq(234) })
);
- verify_that!(
- result,
- err(displays_as(contains_substring(format!(
- "Expected: is {ANENUM_A_REPR} which has all the following properties"
- ))))
- )
-}
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = "Expected: is & AnEnum :: A which has all the following properties";
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = "Expected: is &AnEnum::A which has all the following properties";
+
+ verify_that!(&result, err(displays_as(contains_substring(EXPECTED))))
+}
#[test]
fn includes_struct_name_in_description_with_property() -> Result<()> {
#[derive(Debug)]
@@ -579,16 +693,16 @@
}
let actual = AStruct { field: 123 };
- let result = verify_that!(actual, matches_pattern!(AStruct { get_field(): eq(234) }));
+ let result = verify_that!(actual, matches_pattern!(&AStruct { get_field(): eq(234) }));
- verify_that!(
- result,
- err(displays_as(contains_substring(
- "Expected: is AStruct which has property `get_field ()`"
- )))
- )
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = "Expected: is & AStruct which has property `get_field ()`";
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = "Expected: is &AStruct which has property `get_field ()`";
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
-
#[test]
fn includes_struct_name_in_description_with_ref_property() -> Result<()> {
#[derive(Debug)]
@@ -602,14 +716,15 @@
}
let actual = AStruct { field: 123 };
- let result = verify_that!(actual, matches_pattern!(AStruct { *get_field(): eq(234) }));
+ let result = verify_that!(actual, matches_pattern!(&AStruct { get_field(): eq(&234) }));
- verify_that!(
- result,
- err(displays_as(contains_substring(
- "Expected: is AStruct which has property `get_field ()`"
- )))
- )
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = "Expected: is & AStruct which has property `get_field ()`";
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = "Expected: is &AStruct which has property `get_field ()`";
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
#[test]
@@ -626,14 +741,15 @@
let actual = AStruct { field: 123 };
let result =
- verify_that!(actual, matches_pattern!(AStruct { field: eq(123), get_field(): eq(234) }));
+ verify_that!(actual, matches_pattern!(&AStruct { field: eq(123), get_field(): eq(234) }));
- verify_that!(
- result,
- err(displays_as(contains_substring(
- "Expected: is AStruct which has all the following properties"
- )))
- )
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = "Expected: is & AStruct which has all the following properties";
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = "Expected: is &AStruct which has all the following properties";
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
#[test]
@@ -650,16 +766,16 @@
let actual = AStruct { field: 123 };
let result =
- verify_that!(actual, matches_pattern!(AStruct { field: eq(123), *get_field(): eq(234) }));
+ verify_that!(actual, matches_pattern!(&AStruct { field: eq(123), get_field(): eq(&234) }));
- verify_that!(
- result,
- err(displays_as(contains_substring(
- "Expected: is AStruct which has all the following properties"
- )))
- )
+ #[rustversion::before(1.76)]
+ const EXPECTED: &str = "Expected: is & AStruct which has all the following properties";
+
+ #[rustversion::since(1.76)]
+ const EXPECTED: &str = "Expected: is &AStruct which has all the following properties";
+
+ verify_that!(result, err(displays_as(contains_substring(EXPECTED))))
}
-
#[test]
fn matches_struct_with_a_method() -> Result<()> {
#[derive(Debug)]
@@ -675,9 +791,8 @@
let actual = AStruct { a_field: 123 };
- verify_that!(actual, matches_pattern!(AStruct { get_field(): eq(123) }))
+ verify_that!(actual, matches_pattern!(&AStruct { get_field(): eq(123) }))
}
-
#[test]
fn matches_struct_with_a_method_and_trailing_comma() -> Result<()> {
#[derive(Debug)]
@@ -693,9 +808,8 @@
let actual = AStruct { a_field: 123 };
- verify_that!(actual, matches_pattern!(AStruct { get_field(): eq(123), }))
+ verify_that!(actual, matches_pattern!(&AStruct { get_field(): eq(123), }))
}
-
#[test]
fn matches_struct_with_a_method_taking_parameter() -> Result<()> {
#[derive(Debug)]
@@ -711,9 +825,8 @@
let actual = AStruct { a_field: 1 };
- verify_that!(actual, matches_pattern!(AStruct { add_to_field(2): eq(3) }))
+ verify_that!(actual, matches_pattern!(&AStruct { add_to_field(2): eq(3) }))
}
-
#[test]
fn matches_struct_with_a_method_taking_two_parameters() -> Result<()> {
#[derive(Debug)]
@@ -729,9 +842,8 @@
let actual = AStruct { a_field: 1 };
- verify_that!(actual, matches_pattern!(AStruct { add_product_to_field(2, 3): eq(7) }))
+ verify_that!(actual, matches_pattern!(&AStruct { add_product_to_field(2, 3): eq(7) }))
}
-
#[test]
fn matches_struct_with_a_method_taking_enum_value_parameter() -> Result<()> {
enum AnEnum {
@@ -751,9 +863,8 @@
let actual = AStruct { a_field: 1 };
- verify_that!(actual, matches_pattern!(AStruct { get_a_field(AnEnum::AVariant): eq(1) }))
+ verify_that!(actual, matches_pattern!(&AStruct { get_a_field(AnEnum::AVariant): eq(1) }))
}
-
#[test]
fn matches_struct_with_a_method_taking_two_parameters_with_trailing_comma() -> Result<()> {
#[derive(Debug)]
@@ -769,9 +880,8 @@
let actual = AStruct { a_field: 1 };
- verify_that!(actual, matches_pattern!(AStruct { add_product_to_field(2, 3,): eq(7) }))
+ verify_that!(actual, matches_pattern!(&AStruct { add_product_to_field(2, 3,): eq(7) }))
}
-
#[test]
fn matches_struct_with_a_method_returning_a_reference() -> Result<()> {
#[derive(Debug)]
@@ -787,9 +897,8 @@
let actual = AStruct { a_field: 123 };
- verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(): eq(123) }))
+ verify_that!(actual, matches_pattern!(&AStruct { get_field_ref(): eq(&123) }))
}
-
#[test]
fn matches_struct_with_a_method_returning_a_reference_with_trailing_comma() -> Result<()> {
#[derive(Debug)]
@@ -805,9 +914,8 @@
let actual = AStruct { a_field: 123 };
- verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(): eq(123), }))
+ verify_that!(actual, matches_pattern!(&AStruct { get_field_ref(): eq(&123), }))
}
-
#[test]
fn matches_struct_with_a_method_taking_two_parameters_ret_ref() -> Result<()> {
#[derive(Debug)]
@@ -823,9 +931,8 @@
let actual = AStruct { a_field: 1 };
- verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(2, 3): eq(1) }))
+ verify_that!(actual, matches_pattern!(&AStruct { get_field_ref(2, 3): eq(&1) }))
}
-
#[test]
fn matches_struct_with_a_method_returning_reference_taking_enum_value_parameter() -> Result<()> {
enum AnEnum {
@@ -845,9 +952,8 @@
let actual = AStruct { a_field: 1 };
- verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(AnEnum::AVariant): eq(1) }))
+ verify_that!(actual, matches_pattern!(&AStruct { get_field_ref(AnEnum::AVariant): eq(&1) }))
}
-
#[test]
fn matches_struct_with_a_method_taking_two_parameters_with_trailing_comma_ret_ref() -> Result<()> {
#[derive(Debug)]
@@ -863,9 +969,8 @@
let actual = AStruct { a_field: 1 };
- verify_that!(actual, matches_pattern!(AStruct { *get_field_ref(2, 3,): eq(1) }))
+ verify_that!(actual, matches_pattern!(&AStruct { get_field_ref(2, 3,): eq(&1) }))
}
-
#[test]
fn matches_struct_with_a_method_followed_by_a_field() -> Result<()> {
#[derive(Debug)]
@@ -882,9 +987,11 @@
let actual = AStruct { a_field: 123, another_field: 234 };
- verify_that!(actual, matches_pattern!(AStruct { get_field(): eq(123), another_field: eq(234) }))
+ verify_that!(
+ actual,
+ matches_pattern!(&AStruct { get_field(): eq(123), another_field: eq(234) })
+ )
}
-
#[test]
fn matches_struct_with_a_method_followed_by_a_field_with_trailing_comma() -> Result<()> {
#[derive(Debug)]
@@ -903,10 +1010,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { get_field(): eq(123), another_field: eq(234), })
+ matches_pattern!(&AStruct { get_field(): eq(123), another_field: eq(234), })
)
}
-
#[test]
fn matches_struct_with_a_method_taking_two_parameters_and_field() -> Result<()> {
#[derive(Debug)]
@@ -925,10 +1031,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { add_product_to_field(2, 3): eq(7), another_field: eq(123) })
+ matches_pattern!(&AStruct { add_product_to_field(2, 3): eq(7), another_field: eq(123) })
)
}
-
#[test]
fn matches_struct_with_a_method_taking_enum_value_parameter_followed_by_field() -> Result<()> {
enum AnEnum {
@@ -951,10 +1056,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { get_field(AnEnum::AVariant): eq(1), another_field: eq(2) })
+ matches_pattern!(&AStruct { get_field(AnEnum::AVariant): eq(1), another_field: eq(2) })
)
}
-
#[test]
fn matches_struct_with_a_method_taking_two_parameters_with_trailing_comma_and_field() -> Result<()>
{
@@ -974,10 +1078,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { add_product_to_field(2, 3,): eq(7), another_field: eq(123) })
+ matches_pattern!(&AStruct { add_product_to_field(2, 3,): eq(7), another_field: eq(123) })
)
}
-
#[test]
fn matches_struct_with_a_method_returning_reference_followed_by_a_field() -> Result<()> {
#[derive(Debug)]
@@ -996,13 +1099,12 @@
verify_that!(
actual,
- matches_pattern!(AStruct { *get_field_ref(): eq(123), another_field: eq(234) })
+ matches_pattern!(&AStruct { get_field_ref(): eq(&123), another_field: eq(234) })
)
}
-
#[test]
-fn matches_struct_with_a_method_returning_reference_followed_by_a_field_with_trailing_comma()
--> Result<()> {
+fn matches_struct_with_a_method_returning_reference_followed_by_a_field_with_trailing_comma(
+) -> Result<()> {
#[derive(Debug)]
struct AStruct {
a_field: u32,
@@ -1019,10 +1121,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { *get_field_ref(): eq(123), another_field: eq(234), })
+ matches_pattern!(&AStruct { get_field_ref(): eq(&123), another_field: eq(234), })
)
}
-
#[test]
fn matches_struct_with_a_method_taking_two_parameters_ret_ref_and_field() -> Result<()> {
#[derive(Debug)]
@@ -1041,10 +1142,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { *get_field_ref(2, 3): eq(1), another_field: eq(123) })
+ matches_pattern!(&AStruct { get_field_ref(2, 3): eq(&1), another_field: eq(123) })
)
}
-
#[test]
fn matches_struct_with_a_method_taking_enum_value_param_ret_ref_followed_by_field() -> Result<()> {
enum AnEnum {
@@ -1067,13 +1167,12 @@
verify_that!(
actual,
- matches_pattern!(AStruct { *get_field_ref(AnEnum::AVariant): eq(1), another_field: eq(2) })
+ matches_pattern!(&AStruct { get_field_ref(AnEnum::AVariant): eq(&1), another_field: eq(2) })
)
}
-
#[test]
-fn matches_struct_with_a_method_taking_two_parameters_with_trailing_comma_ret_ref_and_field()
--> Result<()> {
+fn matches_struct_with_a_method_taking_two_parameters_with_trailing_comma_ret_ref_and_field(
+) -> Result<()> {
#[derive(Debug)]
struct AStruct {
a_field: u32,
@@ -1090,10 +1189,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { *get_field_ref(2, 3,): eq(1), another_field: eq(123) })
+ matches_pattern!(&AStruct { get_field_ref(2, 3,): eq(&1), another_field: eq(123) })
)
}
-
#[test]
fn matches_struct_with_a_field_followed_by_a_method() -> Result<()> {
#[derive(Debug)]
@@ -1110,9 +1208,11 @@
let actual = AStruct { a_field: 123, another_field: 234 };
- verify_that!(actual, matches_pattern!(AStruct { another_field: eq(234), get_field(): eq(123) }))
+ verify_that!(
+ actual,
+ matches_pattern!(&AStruct { another_field: eq(234), get_field(): eq(123) })
+ )
}
-
#[test]
fn matches_struct_with_a_field_followed_by_a_method_with_trailing_comma() -> Result<()> {
#[derive(Debug)]
@@ -1131,10 +1231,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { another_field: eq(234), get_field(): eq(123), })
+ matches_pattern!(&AStruct { another_field: eq(234), get_field(): eq(123), })
)
}
-
#[test]
fn matches_struct_with_a_field_followed_by_a_method_with_params() -> Result<()> {
#[derive(Debug)]
@@ -1153,10 +1252,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { another_field: eq(234), add_product_to_field(2, 3): eq(7) })
+ matches_pattern!(&AStruct { another_field: eq(234), add_product_to_field(2, 3): eq(7) })
)
}
-
#[test]
fn matches_struct_with_field_followed_by_method_taking_enum_value_param() -> Result<()> {
enum AnEnum {
@@ -1179,10 +1277,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { another_field: eq(2), get_field(AnEnum::AVariant): eq(1) })
+ matches_pattern!(&AStruct { another_field: eq(2), get_field(AnEnum::AVariant): eq(1) })
)
}
-
#[test]
fn matches_struct_with_a_field_followed_by_a_method_with_params_and_trailing_comma() -> Result<()> {
#[derive(Debug)]
@@ -1201,10 +1298,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { another_field: eq(234), add_product_to_field(2, 3,): eq(7) })
+ matches_pattern!(&AStruct { another_field: eq(234), add_product_to_field(2, 3,): eq(7) })
)
}
-
#[test]
fn matches_struct_with_a_field_followed_by_a_method_returning_reference() -> Result<()> {
#[derive(Debug)]
@@ -1223,10 +1319,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { another_field: eq(234), *get_field_ref(): eq(123) })
+ matches_pattern!(&AStruct { another_field: eq(234), get_field_ref(): eq(&123) })
)
}
-
#[test]
fn matches_struct_with_a_field_followed_by_a_method_returning_ref_and_trailing_comma() -> Result<()>
{
@@ -1246,10 +1341,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { another_field: eq(234), *get_field_ref(): eq(123), })
+ matches_pattern!(&AStruct { another_field: eq(234), get_field_ref(): eq(&123), })
)
}
-
#[test]
fn matches_struct_with_a_field_followed_by_a_method_with_params_ret_ref() -> Result<()> {
#[derive(Debug)]
@@ -1268,10 +1362,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { another_field: eq(234), *get_field_ref(2, 3): eq(123) })
+ matches_pattern!(&AStruct { another_field: eq(234), get_field_ref(2, 3): eq(&123) })
)
}
-
#[test]
fn matches_struct_with_field_followed_by_method_taking_enum_value_param_ret_ref() -> Result<()> {
enum AnEnum {
@@ -1294,13 +1387,12 @@
verify_that!(
actual,
- matches_pattern!(AStruct { another_field: eq(2), *get_field_ref(AnEnum::AVariant): eq(1) })
+ matches_pattern!(&AStruct { another_field: eq(2), get_field_ref(AnEnum::AVariant): eq(&1) })
)
}
-
#[test]
-fn matches_struct_with_a_field_followed_by_a_method_with_params_and_trailing_comma_ret_ref()
--> Result<()> {
+fn matches_struct_with_a_field_followed_by_a_method_with_params_and_trailing_comma_ret_ref(
+) -> Result<()> {
#[derive(Debug)]
struct AStruct {
a_field: u32,
@@ -1317,10 +1409,9 @@
verify_that!(
actual,
- matches_pattern!(AStruct { another_field: eq(234), *get_field_ref(2, 3,): eq(123) })
+ matches_pattern!(&AStruct { another_field: eq(234), get_field_ref(2, 3,): eq(&123) })
)
}
-
#[test]
fn matches_struct_with_a_field_followed_by_a_method_followed_by_a_field() -> Result<()> {
#[derive(Debug)]
@@ -1340,17 +1431,16 @@
verify_that!(
actual,
- matches_pattern!(AStruct {
+ matches_pattern!(&AStruct {
another_field: eq(234),
get_field(): eq(123),
a_third_field: eq(345)
})
)
}
-
#[test]
-fn matches_struct_with_a_field_followed_by_a_method_followed_by_a_field_with_trailing_comma()
--> Result<()> {
+fn matches_struct_with_a_field_followed_by_a_method_followed_by_a_field_with_trailing_comma(
+) -> Result<()> {
#[derive(Debug)]
struct AStruct {
a_field: u32,
@@ -1368,14 +1458,13 @@
verify_that!(
actual,
- matches_pattern!(AStruct {
+ matches_pattern!(&AStruct {
another_field: eq(234),
get_field(): eq(123),
a_third_field: eq(345),
})
)
}
-
#[test]
fn matches_struct_with_a_field_followed_by_a_method_with_params_followed_by_a_field() -> Result<()>
{
@@ -1396,7 +1485,7 @@
verify_that!(
actual,
- matches_pattern!(AStruct {
+ matches_pattern!(&AStruct {
another_field: eq(234),
add_product_to_field(2, 3): eq(7),
a_third_field: eq(345),
@@ -1405,8 +1494,8 @@
}
#[test]
-fn matches_struct_with_a_field_followed_by_a_method_with_params_and_trailing_comma_followed_by_a_field()
--> Result<()> {
+fn matches_struct_with_a_field_followed_by_a_method_with_params_and_trailing_comma_followed_by_a_field(
+) -> Result<()> {
#[derive(Debug)]
struct AStruct {
a_field: u32,
@@ -1424,7 +1513,7 @@
verify_that!(
actual,
- matches_pattern!(AStruct {
+ matches_pattern!(&AStruct {
another_field: eq(234),
add_product_to_field(2, 3,): eq(7),
a_third_field: eq(345),
@@ -1433,8 +1522,8 @@
}
#[test]
-fn matches_struct_with_field_followed_by_method_taking_enum_value_param_followed_by_field()
--> Result<()> {
+fn matches_struct_with_field_followed_by_method_taking_enum_value_param_followed_by_field(
+) -> Result<()> {
enum AnEnum {
AVariant,
}
@@ -1456,7 +1545,7 @@
verify_that!(
actual,
- matches_pattern!(AStruct {
+ matches_pattern!(&AStruct {
another_field: eq(2),
get_field(AnEnum::AVariant): eq(1),
a_third_field: eq(3),
@@ -1483,17 +1572,17 @@
verify_that!(
actual,
- matches_pattern!(AStruct {
+ matches_pattern!(&AStruct {
another_field: eq(234),
- *get_field_ref(): eq(123),
+ get_field_ref(): eq(&123),
a_third_field: eq(345)
})
)
}
#[test]
-fn matches_struct_with_a_field_followed_by_a_method_ret_ref_followed_by_a_field_with_trailing_comma()
--> Result<()> {
+fn matches_struct_with_a_field_followed_by_a_method_ret_ref_followed_by_a_field_with_trailing_comma(
+) -> Result<()> {
#[derive(Debug)]
struct AStruct {
a_field: u32,
@@ -1511,17 +1600,17 @@
verify_that!(
actual,
- matches_pattern!(AStruct {
+ matches_pattern!(&AStruct {
another_field: eq(234),
- *get_field_ref(): eq(123),
+ get_field_ref(): eq(&123),
a_third_field: eq(345),
})
)
}
#[test]
-fn matches_struct_with_a_field_followed_by_a_method_with_params_ret_ref_followed_by_a_field()
--> Result<()> {
+fn matches_struct_with_a_field_followed_by_a_method_with_params_ret_ref_followed_by_a_field(
+) -> Result<()> {
#[derive(Debug)]
struct AStruct {
a_field: u32,
@@ -1539,17 +1628,17 @@
verify_that!(
actual,
- matches_pattern!(AStruct {
+ matches_pattern!(&AStruct {
another_field: eq(234),
- *get_field_ref(2, 3): eq(123),
+ get_field_ref(2, 3): eq(&123),
a_third_field: eq(345),
})
)
}
#[test]
-fn matches_struct_with_field_followed_by_method_taking_enum_value_param_ret_ref_followed_by_field()
--> Result<()> {
+fn matches_struct_with_field_followed_by_method_taking_enum_value_param_ret_ref_followed_by_field(
+) -> Result<()> {
enum AnEnum {
AVariant,
}
@@ -1571,17 +1660,17 @@
verify_that!(
actual,
- matches_pattern!(AStruct {
+ matches_pattern!(&AStruct {
another_field: eq(2),
- *get_field_ref(AnEnum::AVariant): eq(1),
+ get_field_ref(AnEnum::AVariant): eq(&1),
a_third_field: eq(3),
})
)
}
#[test]
-fn matches_struct_with_a_field_followed_by_a_method_with_params_trailing_comma_ret_ref_followed_by_a_field()
--> Result<()> {
+fn matches_struct_with_a_field_followed_by_a_method_with_params_trailing_comma_ret_ref_followed_by_a_field(
+) -> Result<()> {
#[derive(Debug)]
struct AStruct {
a_field: u32,
@@ -1599,10 +1688,129 @@
verify_that!(
actual,
- matches_pattern!(AStruct {
+ matches_pattern!(&AStruct {
another_field: eq(234),
- *get_field_ref(2, 3,): eq(123),
+ get_field_ref(2, 3,): eq(&123),
a_third_field: eq(345),
})
)
}
+
+#[test]
+fn matches_struct_field_copy() -> Result<()> {
+ #[derive(Debug)]
+ struct AStruct {
+ a_field: u32,
+ }
+
+ let actual = AStruct { a_field: 123 };
+
+ verify_that!(actual, matches_pattern!(&AStruct { a_field: eq(123) }))
+}
+
+#[test]
+fn matches_struct_field_non_copy() -> Result<()> {
+ #[derive(Debug)]
+ struct AStruct {
+ a_field: String,
+ }
+
+ let actual = AStruct { a_field: "123".into() };
+
+ verify_that!(
+ actual,
+ matches_pattern!(&AStruct {
+ a_field: ref eq("123"),
+ })
+ )
+}
+
+#[test]
+fn matches_copy_struct_field_copy() -> Result<()> {
+ #[derive(Debug, Clone, Copy)]
+ struct AStruct {
+ a_field: i32,
+ }
+
+ let actual = AStruct { a_field: 123 };
+
+ verify_that!(actual, matches_pattern!(AStruct { a_field: eq(123) }))
+}
+
+#[test]
+fn matches_struct_property_copy() -> Result<()> {
+ #[derive(Debug)]
+ struct AStruct;
+
+ impl AStruct {
+ fn prop(&self) -> i32 {
+ 123
+ }
+ }
+
+ let actual = AStruct;
+
+ verify_that!(actual, matches_pattern!(&AStruct { prop(): eq(123) }))
+}
+
+#[test]
+fn matches_struct_property_non_copy() -> Result<()> {
+ #[derive(Debug)]
+ struct AStruct;
+
+ impl AStruct {
+ fn prop(&self) -> String {
+ "123".into()
+ }
+ }
+
+ let actual = AStruct;
+
+ verify_that!(actual, matches_pattern!(&AStruct { prop(): ref eq("123") }))
+}
+
+#[test]
+fn matches_copy_struct_property_copy() -> Result<()> {
+ #[derive(Debug, Clone, Copy)]
+ struct AStruct;
+
+ impl AStruct {
+ fn prop(self) -> i32 {
+ 123
+ }
+ }
+
+ let actual = AStruct;
+
+ verify_that!(actual, matches_pattern!(AStruct { prop(): eq(123) }))
+}
+
+#[test]
+fn matches_copy_struct_property_non_copy() -> Result<()> {
+ #[derive(Debug, Clone, Copy)]
+ struct AStruct;
+
+ impl AStruct {
+ fn prop(self) -> String {
+ "123".into()
+ }
+ }
+ let actual = AStruct;
+
+ verify_that!(actual, matches_pattern!(AStruct { prop(): ref eq("123") }))
+}
+
+#[test]
+fn matches_struct_auto_eq() -> Result<()> {
+ #[derive(Debug, Clone)]
+ struct AStruct {
+ int: i32,
+ string: String,
+ option: Option<i32>,
+ }
+
+ verify_that!(
+ AStruct { int: 123, string: "123".into(), option: Some(123) },
+ matches_pattern!(&AStruct { int: 123, string: ref "123", option: Some(123) })
+ )
+}
diff --git a/crates/googletest/tests/no_color_test.rs b/crates/googletest/tests/no_color_test.rs
index f307890..84e4cad 100644
--- a/crates/googletest/tests/no_color_test.rs
+++ b/crates/googletest/tests/no_color_test.rs
@@ -12,10 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![cfg(feature = "supports-color")]
-
use googletest::prelude::*;
-use indoc::indoc;
use std::fmt::{Display, Write};
// Make a long text with each element of the iterator on one line.
@@ -35,20 +32,19 @@
std::env::set_var("NO_COLOR", "1");
std::env::set_var("FORCE_COLOR", "1");
- let result = verify_that!(build_text(1..50), eq(build_text(1..51)));
+ let result = verify_that!(build_text(1..50), eq(&build_text(1..51)));
verify_that!(
result,
- err(displays_as(contains_substring(indoc! {
+ err(displays_as(contains_substring(
"
-
- Difference(-actual / +expected):
- 1
- 2
- <---- 45 common lines omitted ---->
- 48
- 49
- +50"
- })))
+ Difference(-actual / +expected):
+ 1
+ 2
+ <---- 45 common lines omitted ---->
+ 48
+ 49
+ +50"
+ )))
)
}
diff --git a/crates/googletest/tests/pointwise_matcher_test.rs b/crates/googletest/tests/pointwise_matcher_test.rs
index cb8ef3b..b7fd7c5 100644
--- a/crates/googletest/tests/pointwise_matcher_test.rs
+++ b/crates/googletest/tests/pointwise_matcher_test.rs
@@ -18,44 +18,64 @@
#[test]
fn pointwise_matches_single_element() -> Result<()> {
let value = vec![1];
- verify_that!(value, pointwise!(lt, vec![2]))
+ verify_that!(value, pointwise!(lt, vec![&2]))
}
#[test]
fn pointwise_matches_two_elements() -> Result<()> {
let value = vec![1, 2];
- verify_that!(value, pointwise!(lt, vec![2, 3]))
+ verify_that!(value, pointwise!(|x| points_to(lt(x)), vec![2, 3]))
+}
+
+#[test]
+fn pointwise_matches_iterator_returning_by_value() -> Result<()> {
+ #[derive(Clone, Copy, Debug)]
+ struct Countdown(i32);
+ impl Iterator for Countdown {
+ type Item = i32;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.0 {
+ 0 => None,
+ x => {
+ self.0 -= 1;
+ Some(x)
+ }
+ }
+ }
+ }
+ verify_that!(Countdown(3), pointwise!(eq, vec![3, 2, 1]))
}
#[test]
fn pointwise_matches_two_elements_with_array() -> Result<()> {
let value = vec![1, 2];
- verify_that!(value, pointwise!(lt, [2, 3]))
+ verify_that!(value, pointwise!(lt, [&2, &3]))
}
#[test]
fn pointwise_matches_two_element_slice() -> Result<()> {
let value = vec![1, 2];
let slice = value.as_slice();
- verify_that!(*slice, pointwise!(lt, [2, 3]))
+ verify_that!(slice, pointwise!(lt, [&2, &3]))
}
#[test]
fn pointwise_does_not_match_value_of_wrong_length() -> Result<()> {
let value = vec![1];
- verify_that!(value, not(pointwise!(lt, vec![2, 3])))
+ verify_that!(value, not(pointwise!(lt, vec![&2, &3])))
}
#[test]
fn pointwise_does_not_match_value_not_matching_in_first_position() -> Result<()> {
let value = vec![1, 2];
- verify_that!(value, not(pointwise!(lt, vec![1, 3])))
+ verify_that!(value, not(pointwise!(lt, vec![&1, &3])))
}
#[test]
fn pointwise_does_not_match_value_not_matching_in_second_position() -> Result<()> {
let value = vec![1, 2];
- verify_that!(value, not(pointwise!(lt, vec![2, 2])))
+ verify_that!(value, not(pointwise!(lt, vec![&2, &2])))
}
#[test]
@@ -64,12 +84,12 @@
pub(super) use super::lt;
}
let value = vec![1];
- verify_that!(value, pointwise!(submodule::lt, vec![2]))
+ verify_that!(value, pointwise!(submodule::lt, vec![&2]))
}
#[test]
fn pointwise_returns_mismatch_when_actual_value_has_wrong_length() -> Result<()> {
- let result = verify_that!(vec![1, 2, 3], pointwise!(eq, vec![1, 2]));
+ let result = verify_that!(vec![1, 2, 3], pointwise!(eq, vec![&1, &2]));
verify_that!(
result,
@@ -88,7 +108,7 @@
#[test]
fn pointwise_returns_mismatch_when_actual_value_does_not_match_on_first_item() -> Result<()> {
- let result = verify_that!(vec![1, 2, 3], pointwise!(eq, vec![2, 2, 3]));
+ let result = verify_that!(vec![1, 2, 3], pointwise!(eq, vec![&2, &2, &3]));
verify_that!(
result,
@@ -108,7 +128,7 @@
#[test]
fn pointwise_returns_mismatch_when_actual_value_does_not_match_on_second_item() -> Result<()> {
- let result = verify_that!(vec![1, 2, 3], pointwise!(eq, vec![1, 3, 3]));
+ let result = verify_that!(vec![1, 2, 3], pointwise!(eq, &vec![1, 3, 3]));
verify_that!(
result,
@@ -127,9 +147,9 @@
}
#[test]
-fn pointwise_returns_mismatch_when_actual_value_does_not_match_on_first_and_second_items()
--> Result<()> {
- let result = verify_that!(vec![1, 2, 3], pointwise!(eq, vec![2, 3, 3]));
+fn pointwise_returns_mismatch_when_actual_value_does_not_match_on_first_and_second_items(
+) -> Result<()> {
+ let result = verify_that!(vec![1, 2, 3], pointwise!(eq, &vec![2, 3, 3]));
verify_that!(
result,
@@ -150,21 +170,21 @@
#[test]
fn pointwise_matches_single_element_with_lambda_expression_with_extra_value() -> Result<()> {
- let value = vec![1.00001f32];
+ let value = [1.00001f32];
verify_that!(value, pointwise!(|v| near(v, 0.0001), vec![1.0]))
}
#[test]
fn pointwise_matches_single_element_with_two_containers() -> Result<()> {
- let value = vec![1.00001f32];
+ let value = [1.00001f32];
verify_that!(value, pointwise!(near, vec![1.0], vec![0.0001]))
}
#[test]
fn pointwise_matches_single_element_with_three_containers() -> Result<()> {
- let value = vec![1.00001f32];
+ let value = [1.00001f32];
verify_that!(
value,
- pointwise!(|v, t, u| near(v, t * u), vec![1.0f32], vec![0.0001f32], vec![0.5f32])
+ pointwise!(|v, t, u| near(v, t + u), vec![1.0f32], vec![0.0001f32], vec![0.5f32])
)
}
diff --git a/crates/googletest/tests/property_matcher_test.rs b/crates/googletest/tests/property_matcher_test.rs
index 7092446..3c9043d 100644
--- a/crates/googletest/tests/property_matcher_test.rs
+++ b/crates/googletest/tests/property_matcher_test.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use googletest::matcher::{Matcher, MatcherResult};
+use googletest::matcher::MatcherResult;
use googletest::prelude::*;
#[derive(Debug)]
@@ -41,33 +41,25 @@
#[test]
fn matches_struct_with_matching_property() -> Result<()> {
let value = SomeStruct { a_property: 10 };
- verify_that!(value, property!(SomeStruct.get_property(), eq(10)))
+ verify_that!(value, property!(&SomeStruct.get_property(), eq(10)))
}
#[test]
fn matches_struct_with_matching_property_with_parameters() -> Result<()> {
let value = SomeStruct { a_property: 10 };
- verify_that!(value, property!(SomeStruct.add_product_to_field(2, 3), eq(16)))
-}
-
-#[test]
-fn matches_struct_with_matching_property_with_captured_arguments() -> Result<()> {
- let value = SomeStruct { a_property: 10 };
- let arg1 = 2;
- let arg2 = 3;
- verify_that!(value, property!(SomeStruct.add_product_to_field(arg1, arg2), eq(16)))
+ verify_that!(value, property!(&SomeStruct.add_product_to_field(2, 3), eq(16)))
}
#[test]
fn matches_struct_with_matching_property_with_parameters_with_trailing_comma() -> Result<()> {
let value = SomeStruct { a_property: 10 };
- verify_that!(value, property!(SomeStruct.add_product_to_field(2, 3,), eq(16)))
+ verify_that!(value, property!(&SomeStruct.add_product_to_field(2, 3,), eq(16)))
}
#[test]
fn matches_struct_with_matching_property_ref() -> Result<()> {
let value = SomeStruct { a_property: 10 };
- verify_that!(value, property!(*SomeStruct.get_property_ref(), eq(10)))
+ verify_that!(value, property!(&SomeStruct.get_property_ref(), eq(&10)))
}
#[test]
@@ -82,7 +74,7 @@
}
}
let value = StructWithString { property: "Something".into() };
- verify_that!(value, property!(*StructWithString.get_property_ref(), eq("Something")))
+ verify_that!(value, property!(&StructWithString.get_property_ref(), eq("Something")))
}
#[test]
@@ -97,31 +89,31 @@
}
}
let value = StructWithVec { property: vec![1, 2, 3] };
- verify_that!(value, property!(*StructWithVec.get_property_ref(), eq([1, 2, 3])))
+ verify_that!(value, property!(&StructWithVec.get_property_ref(), eq([1, 2, 3])))
}
#[test]
fn matches_struct_with_matching_property_ref_with_parameters() -> Result<()> {
let value = SomeStruct { a_property: 10 };
- verify_that!(value, property!(*SomeStruct.get_property_ref_with_params(2, 3), eq(10)))
+ verify_that!(value, property!(&SomeStruct.get_property_ref_with_params(2, 3), eq(&10)))
}
#[test]
fn matches_struct_with_matching_property_ref_with_parameters_and_trailing_comma() -> Result<()> {
let value = SomeStruct { a_property: 10 };
- verify_that!(value, property!(*SomeStruct.get_property_ref_with_params(2, 3,), eq(10)))
+ verify_that!(value, property!(&SomeStruct.get_property_ref_with_params(2, 3,), eq(&10)))
}
#[test]
fn does_not_match_struct_with_non_matching_property() -> Result<()> {
let value = SomeStruct { a_property: 2 };
- verify_that!(value, not(property!(SomeStruct.get_property(), eq(1))))
+ verify_that!(value, not(property!(&SomeStruct.get_property(), eq(1))))
}
#[test]
fn describes_itself_in_matching_case() -> Result<()> {
verify_that!(
- property!(SomeStruct.get_property(), eq(1)).describe(MatcherResult::Match),
+ property!(&SomeStruct.get_property(), eq(1)).describe(MatcherResult::Match),
displays_as(eq("has property `get_property()`, which is equal to 1"))
)
}
@@ -129,20 +121,44 @@
#[test]
fn describes_itself_in_not_matching_case() -> Result<()> {
verify_that!(
- property!(SomeStruct.get_property(), eq(1)).describe(MatcherResult::NoMatch),
+ property!(&SomeStruct.get_property(), eq(1)).describe(MatcherResult::NoMatch),
displays_as(eq("has property `get_property()`, which isn't equal to 1"))
)
}
#[test]
fn explains_mismatch_referencing_explanation_of_inner_matcher() -> Result<()> {
+ #[derive(Debug)]
+ struct SomeStruct;
+
impl SomeStruct {
fn get_a_collection(&self) -> Vec<u32> {
vec![]
}
}
- let value = SomeStruct { a_property: 2 };
- let result = verify_that!(value, property!(SomeStruct.get_a_collection(), container_eq([1])));
+ let value = SomeStruct;
+ let result =
+ verify_that!(value, property!(&SomeStruct.get_a_collection(), ref container_eq([1])));
+
+ verify_that!(
+ result,
+ err(displays_as(contains_substring(
+ "whose property `get_a_collection()` is `[]`, which is missing the element 1"
+ )))
+ )
+}
+
+#[test]
+fn explains_mismatch_referencing_explanation_of_inner_matcher_binding_mode() -> Result<()> {
+ #[derive(Debug)]
+ struct SomeStruct;
+ impl SomeStruct {
+ fn get_a_collection(&self) -> Vec<u32> {
+ vec![]
+ }
+ }
+ let result =
+ verify_that!(SomeStruct, property!(SomeStruct.get_a_collection(), container_eq([1])));
verify_that!(
result,
@@ -155,7 +171,7 @@
#[test]
fn describes_itself_in_matching_case_for_ref() -> Result<()> {
verify_that!(
- property!(*SomeStruct.get_property_ref(), eq(1)).describe(MatcherResult::Match),
+ property!(&SomeStruct.get_property_ref(), eq(&1)).describe(MatcherResult::Match),
displays_as(eq("has property `get_property_ref()`, which is equal to 1"))
)
}
@@ -163,7 +179,7 @@
#[test]
fn describes_itself_in_not_matching_case_for_ref() -> Result<()> {
verify_that!(
- property!(*SomeStruct.get_property_ref(), eq(1)).describe(MatcherResult::NoMatch),
+ property!(&SomeStruct.get_property_ref(), eq(&1)).describe(MatcherResult::NoMatch),
displays_as(eq("has property `get_property_ref()`, which isn't equal to 1"))
)
}
@@ -171,14 +187,16 @@
#[test]
fn explains_mismatch_referencing_explanation_of_inner_matcher_for_ref() -> Result<()> {
static EMPTY_COLLECTION: Vec<u32> = vec![];
+ #[derive(Debug)]
+ struct SomeStruct;
impl SomeStruct {
fn get_a_collection_ref(&self) -> &[u32] {
&EMPTY_COLLECTION
}
}
- let value = SomeStruct { a_property: 2 };
+ let value = SomeStruct;
let result =
- verify_that!(value, property!(*SomeStruct.get_a_collection_ref(), container_eq([1])));
+ verify_that!(value, property!(&SomeStruct.get_a_collection_ref(), container_eq([1])));
verify_that!(
result,
@@ -187,3 +205,94 @@
)))
)
}
+
+#[test]
+fn matches_copy_to_copy() -> Result<()> {
+ #[derive(Debug, Clone, Copy)]
+ struct Struct;
+ impl Struct {
+ fn property(self) -> i32 {
+ 32
+ }
+ }
+
+ verify_that!(Struct, property!(Struct.property(), eq(32)))
+}
+
+#[test]
+fn matches_copy_to_ref() -> Result<()> {
+ #[derive(Debug, Clone, Copy)]
+ struct Struct;
+ impl Struct {
+ fn property(self) -> String {
+ "something".into()
+ }
+ }
+
+ verify_that!(Struct, property!(Struct.property(), ref eq("something")))
+}
+
+#[test]
+fn matches_copy_but_by_ref() -> Result<()> {
+ #[derive(Debug, Clone, Copy)]
+ struct Struct;
+ impl Struct {
+ fn property(&self) -> String {
+ "something".into()
+ }
+ }
+
+ verify_that!(&Struct, property!(&Struct.property(), ref eq("something")))
+}
+
+#[test]
+fn matches_ref_to_ref() -> Result<()> {
+ #[derive(Debug)]
+ struct Struct;
+ impl Struct {
+ fn property(&self) -> String {
+ "something".into()
+ }
+ }
+
+ verify_that!(Struct, property!(&Struct.property(), ref eq("something")))
+}
+
+#[test]
+fn matches_ref_to_copy() -> Result<()> {
+ #[derive(Debug)]
+ struct Struct;
+ impl Struct {
+ fn property(&self) -> i32 {
+ 32
+ }
+ }
+
+ verify_that!(Struct, property!(&Struct.property(), eq(32)))
+}
+
+#[test]
+fn matches_ref_to_ref_with_binding_mode() -> Result<()> {
+ #[derive(Debug)]
+ struct Struct;
+ impl Struct {
+ fn property(&self) -> String {
+ "something".into()
+ }
+ }
+
+ verify_that!(Struct, property!(Struct.property(), eq("something")))
+}
+
+#[test]
+fn matches_property_auto_eq() -> Result<()> {
+ #[derive(Debug)]
+ struct Struct;
+ impl Struct {
+ fn property(&self) -> String {
+ "something".into()
+ }
+ }
+
+ verify_that!(Struct, property!(Struct.property(), "something"))
+}
diff --git a/crates/googletest/tests/tuple_matcher_test.rs b/crates/googletest/tests/tuple_matcher_test.rs
index a93ffee..a19e6fd 100644
--- a/crates/googletest/tests/tuple_matcher_test.rs
+++ b/crates/googletest/tests/tuple_matcher_test.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use googletest::matcher::{Matcher, MatcherResult};
+use googletest::matcher::MatcherResult;
use googletest::prelude::*;
use indoc::indoc;
@@ -22,6 +22,12 @@
}
#[test]
+fn empty_matcher_matches_empty_tuple_reference() -> Result<()> {
+ let a_ok: std::result::Result<(), String> = Ok(()); // Non copy
+ verify_that!(a_ok, ok(()))
+}
+
+#[test]
fn singleton_matcher_matches_matching_singleton_tuple() -> Result<()> {
verify_that!((123,), (eq(123),))
}
@@ -176,7 +182,7 @@
#[test]
fn tuple_matcher_1_has_correct_description_for_match() -> Result<()> {
verify_that!(
- (eq::<i32, _>(1),).describe(MatcherResult::Match),
+ Matcher::<(i32,)>::describe(&(eq(1),), MatcherResult::Match),
displays_as(eq(indoc!(
"
is a tuple whose values respectively match:
@@ -188,7 +194,7 @@
#[test]
fn tuple_matcher_1_has_correct_description_for_mismatch() -> Result<()> {
verify_that!(
- (eq::<i32, _>(1),).describe(MatcherResult::NoMatch),
+ Matcher::<(i32,)>::describe(&(eq(1),), MatcherResult::NoMatch),
displays_as(eq(indoc!(
"
is a tuple whose values do not respectively match:
@@ -200,7 +206,7 @@
#[test]
fn tuple_matcher_2_has_correct_description_for_match() -> Result<()> {
verify_that!(
- (eq::<i32, _>(1), eq::<i32, _>(2)).describe(MatcherResult::Match),
+ Matcher::<(i32, i32)>::describe(&(eq(1), eq(2)), MatcherResult::Match),
displays_as(eq(indoc!(
"
is a tuple whose values respectively match:
@@ -213,7 +219,7 @@
#[test]
fn tuple_matcher_2_has_correct_description_for_mismatch() -> Result<()> {
verify_that!(
- (eq::<i32, _>(1), eq::<i32, _>(2)).describe(MatcherResult::NoMatch),
+ Matcher::<(i32, i32)>::describe(&(eq(1), eq(2)), MatcherResult::NoMatch),
displays_as(eq(indoc!(
"
is a tuple whose values do not respectively match:
@@ -226,7 +232,7 @@
#[test]
fn describe_match_shows_which_tuple_element_did_not_match() -> Result<()> {
verify_that!(
- (eq(1), eq(2)).explain_match(&(1, 3)),
+ (eq(1), eq(2)).explain_match((1, 3)),
displays_as(eq(indoc!(
"
which
@@ -242,7 +248,7 @@
#[test]
fn describe_match_shows_which_two_tuple_elements_did_not_match() -> Result<()> {
verify_that!(
- (eq(1), eq(2)).explain_match(&(2, 3)),
+ (eq(1), eq(2)).explain_match((2, 3)),
displays_as(eq(indoc!(
"
which
diff --git a/crates/googletest/tests/unordered_elements_are_matcher_test.rs b/crates/googletest/tests/unordered_elements_are_matcher_test.rs
index bd61417..0678b0c 100644
--- a/crates/googletest/tests/unordered_elements_are_matcher_test.rs
+++ b/crates/googletest/tests/unordered_elements_are_matcher_test.rs
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use googletest::matcher::Matcher;
use googletest::prelude::*;
use indoc::indoc;
use std::collections::HashMap;
@@ -32,20 +31,40 @@
#[test]
fn unordered_elements_are_matches_vector() -> Result<()> {
let value = vec![1, 2, 3];
- verify_that!(value, unordered_elements_are![eq(1), eq(2), eq(3)])
+ verify_that!(value, unordered_elements_are![eq(&1), eq(&2), eq(&3)])
+}
+
+#[test]
+fn unordered_elements_are_matches_iterator_returning_by_value() -> Result<()> {
+ #[derive(Debug, Copy, Clone)]
+ struct Countdown(i32);
+ impl Iterator for Countdown {
+ type Item = i32;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.0 {
+ 0 => None,
+ x => {
+ self.0 -= 1;
+ Some(x)
+ }
+ }
+ }
+ }
+ verify_that!(Countdown(3), unordered_elements_are![eq(1), eq(2), eq(3)])
}
#[test]
fn unordered_elements_are_omitted() -> Result<()> {
let value = vec![1, 2, 3];
- verify_that!(value, {eq(3), eq(2), eq(1)})
+ verify_that!(value, {eq(&3), eq(&2), eq(&1)})
}
#[test]
fn unordered_elements_are_matches_slice() -> Result<()> {
let value = vec![1, 2, 3];
let slice = value.as_slice();
- verify_that!(*slice, unordered_elements_are![eq(1), eq(2), eq(3)])
+ verify_that!(slice, unordered_elements_are![eq(&1), eq(&2), eq(&3)])
}
#[test]
@@ -53,7 +72,7 @@
let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (3, "Three")]);
verify_that!(
value,
- unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))]
+ unordered_elements_are![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three"))]
)
}
@@ -62,7 +81,7 @@
let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (3, "Three")]);
verify_that!(
value,
- unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three")),]
+ unordered_elements_are![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three")),]
)
}
@@ -71,7 +90,11 @@
let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (4, "Three")]);
verify_that!(
value,
- not(unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))])
+ not(unordered_elements_are![
+ (eq(&2), eq(&"Two")),
+ (eq(&1), eq(&"One")),
+ (eq(&3), eq(&"Three"))
+ ])
)
}
@@ -80,7 +103,11 @@
let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (3, "Four")]);
verify_that!(
value,
- not(unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))])
+ not(unordered_elements_are![
+ (eq(&2), eq(&"Two")),
+ (eq(&1), eq(&"One")),
+ (eq(&3), eq(&"Three"))
+ ])
)
}
@@ -89,14 +116,18 @@
let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two")]);
verify_that!(
value,
- not(unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))])
+ not(unordered_elements_are![
+ (eq(&2), eq(&"Two")),
+ (eq(&1), eq(&"One")),
+ (eq(&3), eq(&"Three"))
+ ])
)
}
#[test]
fn unordered_elements_are_does_not_match_hash_map_with_extra_element() -> Result<()> {
let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (3, "Three")]);
- verify_that!(value, not(unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One"))]))
+ verify_that!(value, not(unordered_elements_are![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One"))]))
}
#[test]
@@ -104,20 +135,24 @@
let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Three"), (3, "Two")]);
verify_that!(
value,
- not(unordered_elements_are![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))])
+ not(unordered_elements_are![
+ (eq(&2), eq(&"Two")),
+ (eq(&1), eq(&"One")),
+ (eq(&3), eq(&"Three"))
+ ])
)
}
#[test]
fn unordered_elements_are_matches_vector_with_trailing_comma() -> Result<()> {
let value = vec![1, 2, 3];
- verify_that!(value, unordered_elements_are![eq(1), eq(2), eq(3),])
+ verify_that!(value, unordered_elements_are![eq(&1), eq(&2), eq(&3),])
}
#[test]
fn unordered_elements_are_matches_size() -> Result<()> {
let value = vec![1, 2];
- verify_that!(value, not(unordered_elements_are![eq(1), eq(2), eq(3)]))
+ verify_that!(value, not(unordered_elements_are![eq(&1), eq(&2), eq(&3)]))
}
#[test]
@@ -125,7 +160,7 @@
#[derive(Debug, PartialEq)]
struct AStruct(i32);
let expected_value = AStruct(123);
- verify_that!(vec![AStruct(123)], unordered_elements_are![eq_deref_of(&expected_value)])
+ verify_that!(vec![AStruct(123)], unordered_elements_are![eq(&expected_value)])
}
#[test]
@@ -135,13 +170,13 @@
let expected_value = AStruct(123);
verify_that!(
HashMap::from([(1, AStruct(123))]),
- unordered_elements_are![(eq(1), eq_deref_of(&expected_value))]
+ unordered_elements_are![(eq(&1), eq(&expected_value))]
)
}
#[test]
fn unordered_elements_are_description_mismatch() -> Result<()> {
- let result = verify_that!(vec![1, 4, 3], unordered_elements_are![eq(1), eq(2), eq(3)]);
+ let result = verify_that!(vec![1, 4, 3], unordered_elements_are![eq(&1), eq(&2), eq(&3)]);
verify_that!(
result,
err(displays_as(contains_substring(indoc!(
@@ -160,27 +195,29 @@
#[test]
fn unordered_elements_are_matches_unordered() -> Result<()> {
let value = vec![1, 2];
- verify_that!(value, unordered_elements_are![eq(2), eq(1)])
+ verify_that!(value, unordered_elements_are![eq(&2), eq(&1)])
}
#[test]
fn unordered_elements_are_matches_unordered_with_repetition() -> Result<()> {
let value = vec![1, 2, 1, 2, 1];
- verify_that!(value, unordered_elements_are![eq(1), eq(1), eq(1), eq(2), eq(2)])
+ verify_that!(value, unordered_elements_are![eq(&1), eq(&1), eq(&1), eq(&2), eq(&2)])
}
#[test]
fn unordered_elements_are_explains_mismatch_due_to_wrong_size() -> Result<()> {
+ let matcher = unordered_elements_are![eq(&2), eq(&3), eq(&4)];
verify_that!(
- unordered_elements_are![eq(2), eq(3), eq(4)].explain_match(&vec![2, 3]),
+ matcher.explain_match(&vec![2, 3]),
displays_as(eq("which has size 2 (expected 3)"))
)
}
#[test]
fn unordered_elements_are_description_no_full_match() -> Result<()> {
+ let matcher = unordered_elements_are![eq(&1), eq(&2), eq(&2)];
verify_that!(
- unordered_elements_are![eq(1), eq(2), eq(2)].explain_match(&vec![1, 1, 2]),
+ matcher.explain_match(&vec![1, 1, 2]),
displays_as(eq(indoc!(
"
which does not have a perfect match with the expected elements. The best match found was:
@@ -194,22 +231,24 @@
#[test]
fn unordered_elements_are_unmatchable_expected_description_mismatch() -> Result<()> {
+ let matcher = unordered_elements_are![eq(&1), eq(&2), eq(&3)];
verify_that!(
- unordered_elements_are![eq(1), eq(2), eq(3)].explain_match(&vec![1, 1, 3]),
+ matcher.explain_match(&vec![1, 1, 3]),
displays_as(eq("which has no element matching the expected element #1"))
)
}
#[test]
fn unordered_elements_are_unmatchable_actual_description_mismatch() -> Result<()> {
+ let matcher = unordered_elements_are![eq(&1), eq(&1), eq(&3)];
verify_that!(
- unordered_elements_are![eq(1), eq(1), eq(3)].explain_match(&vec![1, 2, 3]),
+ matcher.explain_match(&vec![1, 2, 3]),
displays_as(eq("whose element #1 does not match any expected elements"))
)
}
-fn create_matcher() -> impl Matcher<ActualT = Vec<i32>> {
- unordered_elements_are![eq(1)]
+fn create_matcher<'a>() -> impl Matcher<&'a Vec<i32>> {
+ unordered_elements_are![eq(&1)]
}
#[test]
@@ -217,8 +256,8 @@
verify_that!(vec![1], create_matcher())
}
-fn create_matcher_for_map() -> impl Matcher<ActualT = HashMap<i32, i32>> {
- unordered_elements_are![(eq(1), eq(1))]
+fn create_matcher_for_map<'a>() -> impl Matcher<&'a HashMap<i32, i32>> {
+ unordered_elements_are![(eq(&1), eq(&1))]
}
#[test]
@@ -228,24 +267,24 @@
#[test]
fn contains_each_matches_when_one_to_one_correspondence_present() -> Result<()> {
- verify_that!(vec![2, 3, 4], contains_each!(eq(2), eq(3), eq(4)))
+ verify_that!(vec![2, 3, 4], contains_each!(eq(&2), eq(&3), eq(&4)))
}
#[test]
fn contains_each_supports_trailing_comma() -> Result<()> {
- verify_that!(vec![2, 3, 4], contains_each!(eq(2), eq(3), eq(4),))
+ verify_that!(vec![2, 3, 4], contains_each!(eq(&2), eq(&3), eq(&4),))
}
#[test]
fn contains_each_matches_hash_map() -> Result<()> {
let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (3, "Three")]);
- verify_that!(value, contains_each![(eq(2), eq("Two")), (eq(1), eq("One"))])
+ verify_that!(value, contains_each![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One"))])
}
#[test]
fn contains_each_matches_hash_map_with_trailing_comma() -> Result<()> {
let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two"), (3, "Three")]);
- verify_that!(value, contains_each![(eq(2), eq("Two")), (eq(1), eq("One")),])
+ verify_that!(value, contains_each![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One")),])
}
#[test]
@@ -265,42 +304,46 @@
#[test]
fn contains_each_matches_when_excess_elements_present() -> Result<()> {
- verify_that!(vec![1, 2, 3, 4], contains_each!(eq(2), eq(3), eq(4)))
+ verify_that!(vec![1, 2, 3, 4], contains_each!(eq(&2), eq(&3), eq(&4)))
}
#[test]
fn contains_each_does_not_match_when_matchers_are_unmatched() -> Result<()> {
- verify_that!(vec![1, 2, 3], not(contains_each!(eq(2), eq(3), eq(4))))
+ verify_that!(vec![1, 2, 3], not(contains_each!(eq(&2), eq(&3), eq(&4))))
}
#[test]
fn contains_each_explains_mismatch_due_to_wrong_size() -> Result<()> {
+ let matcher = contains_each![eq(&2), eq(&3), eq(&4)];
verify_that!(
- contains_each![eq(2), eq(3), eq(4)].explain_match(&vec![2, 3]),
+ matcher.explain_match(&vec![2, 3]),
displays_as(eq("which has size 2 (expected at least 3)"))
)
}
#[test]
fn contains_each_explains_missing_element_in_mismatch() -> Result<()> {
+ let matcher = contains_each![eq(&2), eq(&3), eq(&4)];
verify_that!(
- contains_each![eq(2), eq(3), eq(4)].explain_match(&vec![1, 2, 3]),
+ matcher.explain_match(&vec![1, 2, 3]),
displays_as(eq("which has no element matching the expected element #2"))
)
}
#[test]
fn contains_each_explains_missing_elements_in_mismatch() -> Result<()> {
+ let matcher = contains_each![eq(&2), eq(&3), eq(&4), eq(&5)];
verify_that!(
- contains_each![eq(2), eq(3), eq(4), eq(5)].explain_match(&vec![0, 1, 2, 3]),
+ matcher.explain_match(&vec![0, 1, 2, 3]),
displays_as(eq("which has no elements matching the expected elements #2, #3"))
)
}
#[test]
fn contains_each_explains_mismatch_due_to_no_graph_matching_found() -> Result<()> {
+ let matcher = contains_each![ge(&2), ge(&2)];
verify_that!(
- contains_each![ge(2), ge(2)].explain_match(&vec![1, 2]),
+ matcher .explain_match(&vec![1, 2]),
displays_as(eq(indoc!(
"
which does not have a superset match with the expected elements. The best match found was:
@@ -324,12 +367,12 @@
#[test]
fn is_contained_in_matches_when_one_to_one_correspondence_present() -> Result<()> {
- verify_that!(vec![2, 3, 4], is_contained_in!(eq(2), eq(3), eq(4)))
+ verify_that!(vec![2, 3, 4], is_contained_in!(eq(&2), eq(&3), eq(&4)))
}
#[test]
fn is_contained_supports_trailing_comma() -> Result<()> {
- verify_that!(vec![2, 3, 4], is_contained_in!(eq(2), eq(3), eq(4),))
+ verify_that!(vec![2, 3, 4], is_contained_in!(eq(&2), eq(&3), eq(&4),))
}
#[test]
@@ -337,7 +380,7 @@
let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two")]);
verify_that!(
value,
- is_contained_in![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three"))]
+ is_contained_in![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three"))]
)
}
@@ -346,53 +389,57 @@
let value: HashMap<u32, &'static str> = HashMap::from([(1, "One"), (2, "Two")]);
verify_that!(
value,
- is_contained_in![(eq(2), eq("Two")), (eq(1), eq("One")), (eq(3), eq("Three")),]
+ is_contained_in![(eq(&2), eq(&"Two")), (eq(&1), eq(&"One")), (eq(&3), eq(&"Three")),]
)
}
#[test]
fn is_contained_in_matches_when_container_is_empty() -> Result<()> {
- verify_that!(vec![], is_contained_in!(eq::<i32, _>(2), eq(3), eq(4)))
+ verify_that!(vec![1; 0], is_contained_in!(eq(&2), eq(&3), eq(&4)))
}
#[test]
fn is_contained_in_matches_when_excess_matchers_present() -> Result<()> {
- verify_that!(vec![3, 4], is_contained_in!(eq(2), eq(3), eq(4)))
+ verify_that!(vec![3, 4], is_contained_in!(eq(&2), eq(&3), eq(&4)))
}
#[test]
fn is_contained_in_does_not_match_when_elements_are_unmatched() -> Result<()> {
- verify_that!(vec![1, 2, 3], not(is_contained_in!(eq(2), eq(3), eq(4))))
+ verify_that!(vec![1, 2, 3], not(is_contained_in!(eq(&2), eq(&3), eq(&4))))
}
#[test]
fn is_contained_in_explains_mismatch_due_to_wrong_size() -> Result<()> {
+ let matcher = is_contained_in![eq(&2), eq(&3)];
verify_that!(
- is_contained_in![eq(2), eq(3)].explain_match(&vec![2, 3, 4]),
+ matcher.explain_match(&vec![2, 3, 4]),
displays_as(eq("which has size 3 (expected at most 2)"))
)
}
#[test]
fn is_contained_in_explains_missing_element_in_mismatch() -> Result<()> {
+ let matcher = is_contained_in![eq(&2), eq(&3), eq(&4)];
verify_that!(
- is_contained_in![eq(2), eq(3), eq(4)].explain_match(&vec![1, 2, 3]),
+ matcher.explain_match(&vec![1, 2, 3]),
displays_as(eq("whose element #0 does not match any expected elements"))
)
}
#[test]
fn is_contained_in_explains_missing_elements_in_mismatch() -> Result<()> {
+ let matcher = is_contained_in![eq(&2), eq(&3), eq(&4), eq(&5)];
verify_that!(
- is_contained_in![eq(2), eq(3), eq(4), eq(5)].explain_match(&vec![0, 1, 2, 3]),
+ matcher.explain_match(&vec![0, 1, 2, 3]),
displays_as(eq("whose elements #0, #1 do not match any expected elements"))
)
}
#[test]
fn is_contained_in_explains_mismatch_due_to_no_graph_matching_found() -> Result<()> {
+ let matcher = is_contained_in![ge(&1), ge(&3)];
verify_that!(
- is_contained_in![ge(1), ge(3)].explain_match(&vec![1, 2]),
+ matcher.explain_match(&vec![1, 2]),
displays_as(eq(indoc!(
"
which does not have a subset match with the expected elements. The best match found was:
@@ -401,3 +448,18 @@
Expected element `is greater than or equal to 3` at index 1 did not match any remaining actual element."))
))
}
+
+#[test]
+fn unordered_elements_are_with_auto_eq() -> Result<()> {
+ verify_that!(vec![3, 4, 2], unordered_elements_are![&2, &3, &4])
+}
+
+#[test]
+fn contains_each_with_auto_eq() -> Result<()> {
+ verify_that!(vec![3, 4, 2], contains_each![&2, &4])
+}
+
+#[test]
+fn is_contained_in_with_auto_eq() -> Result<()> {
+ verify_that!(vec![3, 4, 2], is_contained_in![&1, &2, &3, &4])
+}
diff --git a/pseudo_crate/Cargo.lock b/pseudo_crate/Cargo.lock
index 984e54a..b9bf2c8 100644
--- a/pseudo_crate/Cargo.lock
+++ b/pseudo_crate/Cargo.lock
@@ -216,7 +216,7 @@
"glam",
"glob",
"googletest",
- "googletest_macro 0.13.0",
+ "googletest_macro",
"gpio-cdev",
"grpcio",
"grpcio-compiler",
@@ -2286,11 +2286,11 @@
[[package]]
name = "googletest"
-version = "0.11.0"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee44a233e2e661240ef77ed5de92ea9ecf70d4832963a8582a6681e1517f3673"
+checksum = "dce026f84cdd339bf71be01b24fe67470ee634282f68c1c4b563d00a9f002b05"
dependencies = [
- "googletest_macro 0.11.0",
+ "googletest_macro",
"num-traits",
"regex",
"rustversion",
@@ -2298,16 +2298,6 @@
[[package]]
name = "googletest_macro"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed3057b7d1e628480193188ccb1a7850d034a3add3a350f4ed921b48e287ada9"
-dependencies = [
- "quote 1.0.38",
- "syn 2.0.96",
-]
-
-[[package]]
-name = "googletest_macro"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5070fa86976044fe2b004d874c10af5d1aed6d8f6a72ff93a6eb29cc87048bc"
diff --git a/pseudo_crate/Cargo.toml b/pseudo_crate/Cargo.toml
index e24e8f0..b4595e2 100644
--- a/pseudo_crate/Cargo.toml
+++ b/pseudo_crate/Cargo.toml
@@ -130,7 +130,7 @@
getrandom = "=0.2.15"
glam = "=0.29.2"
glob = "=0.3.2"
-googletest = "=0.11.0"
+googletest = "=0.13.0"
googletest_macro = "=0.13.0"
gpio-cdev = "=0.6.0"
grpcio = "=0.13.0"