target_spec/
proptest_helpers.rs

1// Copyright (c) The cargo-guppy Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::{Platform, TargetFeatures};
5use cfg_expr::targets::ALL_BUILTINS;
6use proptest::{collection::btree_set, prelude::*, sample::select};
7use std::borrow::Cow;
8
9/// ## Helpers for property testing
10///
11/// The methods in this section allow `Platform` instances to be used in property-based testing
12/// scenarios.
13///
14/// Currently, [proptest 1](https://docs.rs/proptest/1) is supported if the `proptest1`
15/// feature is enabled.
16impl Platform {
17    /// Given a way to generate `TargetFeatures` instances, this returns a `Strategy` that generates
18    /// a platform at random.
19    ///
20    /// Requires the `proptest1` feature to be enabled.
21    ///
22    /// ## Examples
23    ///
24    /// ```
25    /// use proptest::prelude::*;
26    /// use target_spec::{Platform, TargetFeatures};
27    ///
28    /// // target_features is a strategy that always produces TargetFeatures::Unknown.
29    /// let target_features = Just(TargetFeatures::Unknown);
30    /// let strategy = Platform::strategy(target_features);
31    /// ```
32    pub fn strategy(
33        target_features: impl Strategy<Value = TargetFeatures>,
34    ) -> impl Strategy<Value = Platform> {
35        let flags = btree_set(flag_strategy(), 0..3);
36        (0..ALL_BUILTINS.len(), target_features, flags).prop_map(|(idx, target_features, flags)| {
37            let mut platform = Platform::new(
38                ALL_BUILTINS[idx].triple.as_str().to_owned(),
39                target_features,
40            )
41            .expect("known triple");
42            platform.add_flags(flags);
43            platform
44        })
45    }
46
47    /// A version of `strategy` that allows target triples to be filtered.
48    ///
49    /// Requires the `proptest1` feature to be enabled.
50    pub fn filtered_strategy(
51        triple_filter: impl Fn(&'static str) -> bool,
52        target_features: impl Strategy<Value = TargetFeatures>,
53    ) -> impl Strategy<Value = Platform> {
54        let filtered: Vec<_> = ALL_BUILTINS
55            .iter()
56            .filter(|target_info| triple_filter(target_info.triple.as_str()))
57            .collect();
58        let flags = btree_set(flag_strategy(), 0..3);
59        (0..filtered.len(), target_features, flags).prop_map(
60            move |(idx, target_features, flags)| {
61                let mut platform = Platform::new(filtered[idx].triple.as_str(), target_features)
62                    .expect("known triple");
63                platform.add_flags(flags);
64                platform
65            },
66        )
67    }
68}
69
70/// Picks a random flag from a list of known flags.
71pub fn flag_strategy() -> impl Strategy<Value = &'static str> {
72    static KNOWN_FLAGS: &[&str] = &["cargo_web", "test-flag", "abc", "foo", "bar", "flag-test"];
73    select(KNOWN_FLAGS)
74}
75
76/// The `Arbitrary` implementation for `TargetFeatures` uses a predefined list of features.
77impl Arbitrary for TargetFeatures {
78    type Parameters = ();
79    type Strategy = BoxedStrategy<Self>;
80
81    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
82        // https://doc.rust-lang.org/reference/attributes/codegen.html#available-features
83        static KNOWN_FEATURES: &[&str] = &[
84            "aes", "avx", "avx2", "bmi1", "bmi2", "fma", "rdrand", "sha", "sse", "sse2", "sse3",
85            "sse4.1", "sse4.2", "ssse3", "xsave", "xsavec", "xsaveopt", "xsaves",
86        ];
87        let known_features_strategy = select(KNOWN_FEATURES).prop_map(Cow::Borrowed);
88        prop_oneof![
89            Just(TargetFeatures::Unknown),
90            Just(TargetFeatures::All),
91            btree_set(known_features_strategy, 0..8).prop_map(TargetFeatures::Features),
92        ]
93        .boxed()
94    }
95}