proptest/arbitrary/_std/
env.rs

1//-
2// Copyright 2017, 2018 The proptest developers
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! Arbitrary implementations for `std::env`.
11
12use std::env::*;
13use std::ffi::OsString;
14use std::iter::once;
15
16use crate::arbitrary::*;
17use crate::strategy::statics::static_map;
18use crate::strategy::*;
19
20// FIXME: SplitPaths when lifetimes in strategies are possible.
21
22lazy_just!(
23    Args, args;
24    ArgsOs, args_os;
25    Vars, vars;
26    VarsOs, vars_os;
27    JoinPathsError, jpe
28);
29
30#[cfg(not(target_os = "windows"))]
31fn jpe() -> JoinPathsError {
32    join_paths(once(":")).unwrap_err()
33}
34
35#[cfg(target_os = "windows")]
36fn jpe() -> JoinPathsError {
37    join_paths(once("\"")).unwrap_err()
38}
39
40// Algorithm from: https://stackoverflow.com/questions/47749164
41#[cfg(any(target_os = "windows", test))]
42fn make_utf16_invalid(buf: &mut [u16], p: usize) {
43    // Verify that length is non-empty.
44    // An empty string is always valid UTF-16.
45    assert!(buf.len() > 0);
46
47    // If first elem or previous entry is not a leading surrogate.
48    let gen_trail = 0 == p || 0xd800 != (buf[p - 1] & 0xfc00);
49    // If last element or succeeding entry is not a traililng surrogate.
50    let gen_lead = p == buf.len() - 1 || 0xdc00 != (buf[p + 1] & 0xfc00);
51    let (force_bits_mask, force_bits_value) = if gen_trail {
52        if gen_lead {
53            // Trailing or leading surrogate.
54            (0xf800, 0xd800)
55        } else {
56            // Trailing surrogate.
57            (0xfc00, 0xdc00)
58        }
59    } else {
60        // Leading surrogate.
61        // Note that `gen_lead` and `gen_trail` could both be false here if `p`
62        // lies exactly between a leading and a trailing surrogate. In this
63        // case, it doesn't matter what we do because the UTF-16 will be
64        // invalid regardless, so just always force a leading surrogate.
65        (0xfc00, 0xd800)
66    };
67    debug_assert_eq!(0, (force_bits_value & !force_bits_mask));
68    buf[p] = (buf[p] & !force_bits_mask) | force_bits_value;
69}
70
71#[cfg(not(target_arch = "wasm32"))]
72mod var_error {
73    use super::*;
74
75    /// Generates the set of `WTF-16 \ UTF-16` and makes
76    /// an `OsString` that is not a valid String from it.
77    #[cfg(target_os = "windows")]
78    fn osstring_invalid_string() -> impl Strategy<Value = OsString> {
79        use std::os::windows::ffi::OsStringExt;
80        let size = 1..::std::u16::MAX as usize;
81        let vec_gen = crate::collection::vec(..::std::u16::MAX, size.clone());
82        (size, vec_gen).prop_map(|(p, mut sbuf)| {
83            // Not quite a uniform distribution due to clamping,
84            // but probably good enough
85            let p = ::std::cmp::min(p, sbuf.len() - 1);
86            make_utf16_invalid(&mut sbuf, p);
87            OsString::from_wide(sbuf.as_slice())
88                .into_string()
89                .unwrap_err()
90        })
91    }
92
93    #[cfg(not(target_os = "windows"))]
94    fn osstring_invalid_string() -> impl Strategy<Value = OsString> {
95        use crate::arbitrary::_std::string::not_utf8_bytes;
96        use std::os::unix::ffi::OsStringExt;
97        static_map(not_utf8_bytes(true), OsString::from_vec)
98    }
99
100    arbitrary!(VarError,
101        TupleUnion<(
102            WA<Just<Self>>,
103            WA<SFnPtrMap<BoxedStrategy<OsString>, Self>>
104        )>;
105        prop_oneof![
106            Just(VarError::NotPresent),
107            static_map(osstring_invalid_string().boxed(), VarError::NotUnicode)
108        ]
109    );
110}
111
112#[cfg(test)]
113mod test {
114    use super::*;
115    use crate::num;
116    use crate::test_runner::Config;
117
118    no_panic_test!(
119        args => Args,
120        args_os => ArgsOs,
121        vars => Vars,
122        vars_os => VarsOs,
123        join_paths_error => JoinPathsError,
124        var_error => VarError
125    );
126
127    proptest! {
128        #![proptest_config(Config {
129            cases: 65536,
130            .. Config::default()
131        })]
132
133        #[test]
134        fn make_utf16_invalid_doesnt_panic(
135            mut buf in [num::u16::ANY; 3],
136            p in 0usize..3
137        ) {
138            make_utf16_invalid(&mut buf, p);
139        }
140    }
141}