diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2020-06-18 02:19:43 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2020-06-26 20:48:49 -0400 |
| commit | 2f4c89cd3133517f58bc9e42f29220a1bbb66bcc (patch) | |
| tree | 2933d4468fddda7c03339bf454c8e3c7e8cab898 /src | |
| parent | ce432438c8be147135f6c56bdd3229834bff2aa6 (diff) | |
Switch from quickcheck to proptest.
The argument is as given in the proptest docs at
<https://altsysrq.github.io/proptest-book/proptest/vs-quickcheck.html>.
I've found that the resulting tests are somewhat clearer, and that the
tools for working with test case generation are more useful.
The other killer feature is recalling test failure examples from run to
run. This change includes at least one bug found while testing the port!
Finally, if <https://github.com/AltSysrq/proptest/issues/179> is to be
believed, proptest is considerably closer to supporting async tests.
Diffstat (limited to 'src')
| -rw-r--r-- | src/twelve.rs | 127 |
1 files changed, 61 insertions, 66 deletions
diff --git a/src/twelve.rs b/src/twelve.rs index 273b17f..7803450 100644 --- a/src/twelve.rs +++ b/src/twelve.rs @@ -8,6 +8,8 @@ //! [1]: https://12factor.net/ //! [2]: https://12factor.net/config +#[cfg(test)] +use proptest_derive::Arbitrary; use std::env; use std::io; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; @@ -47,6 +49,7 @@ pub enum Error { /// Listening on this address will bind to both the ip4 and ip6 addresses on the /// current host, assuming both ip4 and ip6 are supported. #[derive(Debug, Clone)] +#[cfg_attr(test, derive(Arbitrary))] pub struct PortAddr { /// When used in an std::net::SocketAddr context, this is the port number to /// bind on. @@ -104,8 +107,7 @@ pub fn port(default_port: u16) -> Result<PortAddr, Error> { #[cfg(test)] mod tests { use lazy_static::lazy_static; - use quickcheck::{Arbitrary, Gen, TestResult}; - use quickcheck_macros::quickcheck; + use proptest::prelude::*; use std::env; use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; @@ -113,39 +115,33 @@ mod tests { use super::*; - impl Arbitrary for PortAddr { - fn arbitrary<G: Gen>(g: &mut G) -> Self { - Self { - port: u16::arbitrary(g), - } - } - } - - #[quickcheck] - fn port_addr_as_socket_addr_has_v4(addr: PortAddr) -> bool { - let socket_addrs = addr.to_socket_addrs().unwrap().collect::<Vec<_>>(); + proptest! { + #[test] + fn port_addr_as_socket_addr_has_v4(addr: PortAddr) { + let socket_addrs: Vec<_> = addr.to_socket_addrs().unwrap().collect(); - socket_addrs - .iter() - .any(|&socket_addr| socket_addr.is_ipv4()) - } + assert!(socket_addrs + .iter() + .any(|&socket_addr| socket_addr.is_ipv4())); + } - #[quickcheck] - fn port_addr_as_socket_addr_has_v6(addr: PortAddr) -> bool { - let socket_addrs = addr.to_socket_addrs().unwrap().collect::<Vec<_>>(); + #[test] + fn port_addr_as_socket_addr_has_v6(addr: PortAddr) { + let socket_addrs: Vec<_> = addr.to_socket_addrs().unwrap().collect(); - socket_addrs - .iter() - .any(|&socket_addr| socket_addr.is_ipv6()) - } + assert!(socket_addrs + .iter() + .any(|&socket_addr| socket_addr.is_ipv6())); + } - #[quickcheck] - fn port_addr_as_socket_addr_all_have_port(addr: PortAddr) -> bool { - let socket_addrs = addr.to_socket_addrs().unwrap().collect::<Vec<_>>(); + #[test] + fn port_addr_as_socket_addr_all_have_port(addr: PortAddr) { + let socket_addrs: Vec<_> = addr.to_socket_addrs().unwrap().collect(); - socket_addrs - .iter() - .all(|&socket_addr| socket_addr.port() == addr.port) + assert!(socket_addrs + .iter() + .all(|&socket_addr| socket_addr.port() == addr.port)); + } } #[derive(Default)] @@ -161,10 +157,10 @@ mod tests { lazy_static! { // The tests in this module manipulate a global, shared, external - // resource (the PORT environment variable). The quickcheck tool - // attempts to accelerate testing by running multiple threads, but this - // causes race conditions as test A stomps on state used by test B. - // Serialize tests through a mutex. + // resource (the PORT environment variable). The proptest tool attempts + // to accelerate testing by running multiple threads, but this causes + // race conditions as test A stomps on state used by test B. Serialize + // tests through a mutex. // // Huge hack. static ref ENV_MUTEX: Mutex<Runner> = Mutex::new(Runner::default()); @@ -175,48 +171,47 @@ mod tests { ENV_MUTEX.lock().unwrap().run(f) } - #[quickcheck] - fn port_preserves_numeric_values(env_port: u16, default_port: u16) -> TestResult { - if env_port == default_port { - return TestResult::discard(); - } + proptest! { + #[test] + fn port_preserves_numeric_values(env_port: u16, default_port: u16) { + prop_assume!(env_port != default_port); - env_locked(|| { - env::set_var("PORT", env_port.to_string()); + env_locked(|| { + env::set_var("PORT", env_port.to_string()); - let read_port = port(default_port).unwrap(); + let read_port = port(default_port).unwrap(); - TestResult::from_bool(read_port.port == env_port) - }) - } - - #[quickcheck] - fn port_rejects_strings(env_port: String, default_port: u16) -> TestResult { - if env_port.contains("\x00") { - return TestResult::discard(); - } - if env_port.parse::<u16>().is_ok() { - return TestResult::discard(); + assert_eq!(read_port.port, env_port); + }); } - env_locked(|| { - env::set_var("PORT", env_port.to_string()); + #[test] + fn port_rejects_strings(env_port: String, default_port: u16) { + // Reject any sample with a NUL byte; env::set_var (well, libc) + // can't cope. + prop_assume!(!env_port.contains("\x00")); + // Reject any sample that _should_ parse cleanly. + prop_assume!(env_port.parse::<u16>().is_err()); - let port_result = port(default_port); + env_locked(|| { + env::set_var("PORT", env_port.to_string()); - TestResult::from_bool(port_result.is_err()) - }) - } + let port_result = port(default_port); - #[quickcheck] - fn port_uses_default(default_port: u16) -> bool { - env_locked(|| { - env::remove_var("PORT"); + assert!(port_result.is_err()); + }); + } - let read_port = port(default_port).unwrap(); + #[test] + fn port_uses_default(default_port: u16) { + env_locked(|| { + env::remove_var("PORT"); - read_port.port == default_port - }) + let read_port = port(default_port).unwrap(); + + assert_eq!(default_port, read_port.port); + }); + } } #[test] |
