Skip to content

Commit

Permalink
add proptest feature
Browse files Browse the repository at this point in the history
  • Loading branch information
doug-q committed May 2, 2024
1 parent 78cebf8 commit c17885d
Show file tree
Hide file tree
Showing 21 changed files with 611 additions and 551 deletions.
8 changes: 5 additions & 3 deletions hugr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ bench = false
path = "src/lib.rs"

[features]
default = []
extension_inference = []
proptest = ["dep:proptest","dep:proptest-derive","dep:regex-syntax"]

[dependencies]
portgraph = { workspace = true, features = ["serde", "petgraph"] }
Expand Down Expand Up @@ -52,6 +54,9 @@ delegate = "0.12.0"
paste = "1.0"
strum = "0.26.1"
strum_macros = "0.26.1"
proptest = { version = "1.4.0", optional = true }
proptest-derive = { version = "0.4.0", optional = true}
regex-syntax = { version = "0.8.3", optional = true}

[dev-dependencies]
criterion = { version = "0.5.1", features = ["html_reports"] }
Expand All @@ -61,9 +66,6 @@ urlencoding = "2.1.2"
cool_asserts = "2.0.3"
insta = { workspace = true, features = ["yaml"] }
jsonschema = "0.17.1"
proptest = "1.4.0"
proptest-derive = "0.4.0"
regex-syntax = "0.8.3"

[[bench]]
name = "bench_main"
Expand Down
46 changes: 25 additions & 21 deletions hugr/src/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,27 +554,31 @@ impl FromIterator<ExtensionId> for ExtensionSet {
#[cfg(test)]
mod test {

use proptest::{collection::hash_set, prelude::*};

use crate::extension::ExtensionId;
impl Arbitrary for super::ExtensionSet {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
let vars = hash_set(0..10usize, 0..3);
let extensions = hash_set(any::<ExtensionId>(), 0..3);
(vars, extensions)
.prop_map(|(vars, extensions)| {
let mut r = Self::new();
for v in vars {
r.insert_type_var(v);
}
for e in extensions {
r.insert(&e)
}
r
})
.boxed()
#[cfg(feature = "proptest")]
mod proptest {

use ::proptest::{collection::hash_set, prelude::*};

use super::super::{ExtensionId, ExtensionSet};
impl Arbitrary for ExtensionSet {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
let vars = hash_set(0..10usize, 0..3);
let extensions = hash_set(any::<ExtensionId>(), 0..3);
(vars, extensions)
.prop_map(|(vars, extensions)| {
let mut r = Self::new();
for v in vars {
r.insert_type_var(v);
}
for e in extensions {
r.insert(&e)
}
r
})
.boxed()
}
}
}
}
100 changes: 1 addition & 99 deletions hugr/src/hugr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pub mod hugrmut;

mod ident;
pub(crate) mod ident;
pub mod rewrite;
pub mod serialize;
pub mod validate;
Expand Down Expand Up @@ -349,104 +349,6 @@ pub(crate) mod test {
#[cfg(feature = "extension_inference")]
use std::error::Error;

pub(crate) mod proptest {
use lazy_static::lazy_static;
use proptest::collection::vec;
use proptest::strategy::Strategy;

use proptest::prelude::*;
use smol_str::SmolStr;

lazy_static! {
static ref ANY_IDENT_STRING: SBoxedStrategy<String> = {
use proptest::string::string_regex;
prop_oneof![
string_regex(r"[[:alpha:]]{1,3}").unwrap(),
string_regex(crate::hugr::ident::PATH_COMPONENT_NICE_REGEX_STR).unwrap(),
string_regex(crate::hugr::ident::PATH_COMPONENT_REGEX_STR).unwrap(),
].sboxed()
};

static ref ANY_NONEMPTY_STRING: SBoxedStrategy<String> = {
use proptest::string::string_regex;
prop_oneof![
string_regex(r"[[:alpha:]]{1,3}").unwrap(),
string_regex(r"[[:alpha:]]+").unwrap(),
string_regex(r".+").unwrap(),
].sboxed()
};

static ref ANY_STRING: SBoxedStrategy<String> = {
use proptest::string::string_regex;
prop_oneof![
string_regex(r"[[:alpha:]]{0,3}").unwrap(),
string_regex(r"[[:alpha:]]*").unwrap(),
string_regex(r".*").unwrap(),
].sboxed()
};

static ref ANY_SERDE_YAML_VALUE_LEAF: SBoxedStrategy<serde_yaml::Value> = {
use serde_yaml::value::Value;
prop_oneof![
Just(Value::Null),
any::<bool>().prop_map_into(),
any::<u64>().prop_map_into(),
any::<i64>().prop_map_into(),
// any::<f64>().prop_map_into(),
Just(Value::Number(3.into())),
any_string().prop_map_into(),
].sboxed()
};
// .prop_recursive(
// 3, // No more than 3 branch levels deep
// 32, // Target around 32 total elements
// 3, // Each collection is up to 3 elements long
// |element| prop_oneof![
// (any_string().prop_map(Tag::new), element.clone()).prop_map(|(tag, value)| Value::Tagged(Box::new(TaggedValue { tag, value }))),
// proptest::collection::vec(element.clone(), 0..3).prop_map_into(),
// ]
// ).sboxed()
}

pub fn any_nonempty_string() -> SBoxedStrategy<String> {
ANY_NONEMPTY_STRING.clone()
}

pub fn any_nonempty_smolstr() -> SBoxedStrategy<SmolStr> {
ANY_NONEMPTY_STRING.clone().prop_map_into().sboxed()
}

pub fn any_ident_string() -> SBoxedStrategy<String> {
ANY_IDENT_STRING.clone()
}

pub fn any_string() -> SBoxedStrategy<String> {
ANY_STRING.clone()
}

pub fn any_serde_yaml_value() -> impl Strategy<Value = serde_yaml::Value> {
// use serde_yaml::value::{Tag, TaggedValue, Value};
ANY_SERDE_YAML_VALUE_LEAF
.clone()
.prop_recursive(
3, // No more than 3 branch levels deep
32, // Target around 32 total elements
3, // Each collection is up to 3 elements long
|element| {
prop_oneof![
// TaggedValue doesn't roundtrip through JSON
// (any_nonempty_string().prop_map(Tag::new), element.clone()).prop_map(|(tag, value)| Value::Tagged(Box::new(TaggedValue { tag, value }))),
proptest::collection::vec(element.clone(), 0..3).prop_map_into(),
vec((any_string().prop_map_into(), element.clone()), 0..3).prop_map(
|x| x.into_iter().collect::<serde_yaml::Mapping>().into()
)
]
},
)
.boxed()
}
}

#[test]
fn impls_send_and_sync() {
// Send and Sync are automatically impl'd by the compiler, if possible.
Expand Down
64 changes: 34 additions & 30 deletions hugr/src/hugr/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use smol_str::SmolStr;
use thiserror::Error;

pub static PATH_COMPONENT_REGEX_STR: &str = r"[\w--\d]\w*";
#[cfg(test)]
#[cfg(all(test, feature = "proptest"))]
pub static PATH_COMPONENT_NICE_REGEX_STR: &str = r"[[:alpha:]][[[:alpha:]]0-9]*";
lazy_static! {
pub static ref PATH_REGEX: Regex =
Expand Down Expand Up @@ -80,7 +80,39 @@ pub struct InvalidIdentifier(SmolStr);

#[cfg(test)]
mod test {
use proptest::prelude::*;

#[cfg(feature = "proptest")]
mod proptest {
use crate::hugr::ident::IdentList;
use ::proptest::prelude::*;
impl Arbitrary for super::IdentList {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
use crate::proptest::any_ident_string;
use proptest::collection::vec;
// we shrink to more readable (i.e. :alpha:) names
vec(any_ident_string(), 1..2)
.prop_map(|vs| {
IdentList::new(
itertools::intersperse(
vs.into_iter().map(Into::<String>::into),
".".into(),
)
.collect::<String>(),
)
.unwrap()
})
.boxed()
}
}
proptest! {
#[test]
fn arbitrary_identlist_valid((IdentList(ident_list)): IdentList) {
assert!(IdentList::new(ident_list).is_ok())
}
}
}

use super::IdentList;

Expand All @@ -98,32 +130,4 @@ mod test {
IdentList::new("foo..bar").unwrap_err();
IdentList::new(".foo").unwrap_err();
}

impl Arbitrary for super::IdentList {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
use crate::hugr::test::proptest::any_ident_string;
use proptest::collection::vec;
// we shrink to more readable (i.e. :alpha:) names
vec(any_ident_string(), 1..2)
.prop_map(|vs| {
IdentList::new(
itertools::intersperse(
vs.into_iter().map(Into::<String>::into),
".".into(),
)
.collect::<String>(),
)
.unwrap()
})
.boxed()
}
}
proptest! {
#[test]
fn arbitrary_identlist_valid((IdentList(ident_list)): IdentList) {
assert!(IdentList::new(ident_list).is_ok())
}
}
}
47 changes: 28 additions & 19 deletions hugr/src/hugr/serialize/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ use crate::extension::{EMPTY_REG, PRELUDE_REGISTRY};
use crate::hugr::hugrmut::sealed::HugrMutInternals;
use crate::hugr::NodeType;
use crate::ops::custom::{ExtensionOp, OpaqueOp};
use crate::ops::Value;
use crate::ops::{dataflow::IOTrait, Input, Module, Noop, Output, DFG};
use crate::std_extensions::arithmetic::float_ops::FLOAT_OPS_REGISTRY;
use crate::std_extensions::arithmetic::float_types::{ConstF64, FLOAT64_TYPE};

use crate::std_extensions::logic::NotOp;

use crate::types::{FunctionType, PolyFuncType, Type};
use crate::types::{FunctionType, Type};
use crate::{type_row, OutgoingPort};
use itertools::Itertools;
use jsonschema::{Draft, JSONSchema};
Expand All @@ -25,7 +24,6 @@ use portgraph::LinkView;
use portgraph::{
multiportgraph::MultiPortGraph, Hierarchy, LinkMut, PortMut, PortView, UnmanagedDenseMap,
};
use proptest::prelude::*;

const NAT: Type = crate::extension::prelude::USIZE_T;
const QB: Type = crate::extension::prelude::QB_T;
Expand Down Expand Up @@ -171,6 +169,7 @@ pub fn check_hugr_roundtrip(hugr: &Hugr, check_schema: bool) -> Hugr {
new_hugr
}

#[allow(unused)]
fn check_testing_roundtrip(t: impl Into<TestingModel>) {
let before = Versioned::new(t.into());
let after_strict = ser_roundtrip_validate(&before, Some(&TESTING_SCHEMA_STRICT));
Expand Down Expand Up @@ -368,24 +367,34 @@ fn serialize_types_roundtrip() {
assert_eq!(ser_roundtrip(&t), t);
}

proptest! {
#[test]
fn prop_roundtrip_type(t: Type) {
check_testing_roundtrip(t)
}
#[cfg(feature = "proptest")]
mod proptest {
use super::super::NodeSer;
use super::check_testing_roundtrip;
use crate::extension::ExtensionSet;
use crate::ops::{OpType, Value};
use crate::types::{PolyFuncType, Type};
use proptest::prelude::*;

proptest! {
#[test]
fn prop_roundtrip_type(t: Type) {
check_testing_roundtrip(t)
}

#[test]
fn prop_roundtrip_poly_func_type(t: PolyFuncType) {
check_testing_roundtrip(t)
}
#[test]
fn prop_roundtrip_poly_func_type(t: PolyFuncType) {
check_testing_roundtrip(t)
}

#[test]
fn prop_roundtrip_value(t: Value) {
check_testing_roundtrip(t)
}
#[test]
fn prop_roundtrip_value(t: Value) {
check_testing_roundtrip(t)
}

#[test]
fn prop_roundtrip_optype(op in ((0..(std::u32::MAX / 2) as usize).prop_map(|x| portgraph::NodeIndex::new(x).into()), any::<Option<ExtensionSet>>(), any::<OpType>()).prop_map(|(parent, input_extensions, op)| NodeSer { parent, input_extensions, op })) {
check_testing_roundtrip(op)
#[test]
fn prop_roundtrip_optype(op in ((0..(std::u32::MAX / 2) as usize). prop_map(|x| portgraph::NodeIndex::new(x).into()), any::<Option<ExtensionSet>>(), any::<OpType>()).prop_map(|(parent, input_extensions, op)| NodeSer { parent, input_extensions, op })) {
check_testing_roundtrip(op)
}
}
}
3 changes: 3 additions & 0 deletions hugr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,6 @@ pub use crate::core::{
};
pub use crate::extension::Extension;
pub use crate::hugr::{Hugr, HugrView, SimpleReplacement};

#[cfg(all(feature = "proptest", test))]
pub mod proptest;
2 changes: 1 addition & 1 deletion hugr/src/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub use tag::OpTag;

#[enum_dispatch(OpTrait, NamedOp, ValidateOp, OpParent)]
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
#[cfg_attr(all(test, feature = "proptest"), derive(proptest_derive::Arbitrary))]
/// The concrete operation types for a node in the HUGR.
// TODO: Link the NodeHandles to the OpType.
#[non_exhaustive]
Expand Down
Loading

0 comments on commit c17885d

Please sign in to comment.